/src/haproxy/src/sample.c
Line | Count | Source |
1 | | /* |
2 | | * Sample management functions. |
3 | | * |
4 | | * Copyright 2009-2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr> |
5 | | * Copyright (C) 2012 Willy Tarreau <w@1wt.eu> |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU General Public License |
9 | | * as published by the Free Software Foundation; either version |
10 | | * 2 of the License, or (at your option) any later version. |
11 | | * |
12 | | */ |
13 | | |
14 | | #include <ctype.h> |
15 | | #include <string.h> |
16 | | #include <arpa/inet.h> |
17 | | #include <stdio.h> |
18 | | |
19 | | #include <import/mjson.h> |
20 | | #include <import/sha1.h> |
21 | | |
22 | | #include <haproxy/acl.h> |
23 | | #include <haproxy/api.h> |
24 | | #include <haproxy/arg.h> |
25 | | #include <haproxy/auth.h> |
26 | | #include <haproxy/base64.h> |
27 | | #include <haproxy/buf.h> |
28 | | #include <haproxy/cfgparse.h> |
29 | | #include <haproxy/chunk.h> |
30 | | #include <haproxy/clock.h> |
31 | | #include <haproxy/errors.h> |
32 | | #include <haproxy/fix.h> |
33 | | #include <haproxy/global.h> |
34 | | #include <haproxy/hash.h> |
35 | | #include <haproxy/http.h> |
36 | | #include <haproxy/http_ana-t.h> |
37 | | #include <haproxy/istbuf.h> |
38 | | #include <haproxy/mqtt.h> |
39 | | #include <haproxy/net_helper.h> |
40 | | #include <haproxy/protobuf.h> |
41 | | #include <haproxy/proxy.h> |
42 | | #include <haproxy/quic_tune.h> |
43 | | #include <haproxy/regex.h> |
44 | | #include <haproxy/sample.h> |
45 | | #include <haproxy/sc_strm.h> |
46 | | #include <haproxy/sink.h> |
47 | | #include <haproxy/stick_table.h> |
48 | | #include <haproxy/time.h> |
49 | | #include <haproxy/tools.h> |
50 | | #include <haproxy/uri_auth-t.h> |
51 | | #include <haproxy/vars.h> |
52 | | #include <haproxy/xxhash.h> |
53 | | #include <haproxy/jwt.h> |
54 | | |
55 | | /* sample type names */ |
56 | | const char *smp_to_type[SMP_TYPES] = { |
57 | | [SMP_T_ANY] = "any", |
58 | | [SMP_T_SAME] = "same", |
59 | | [SMP_T_BOOL] = "bool", |
60 | | [SMP_T_SINT] = "sint", |
61 | | [SMP_T_ADDR] = "addr", |
62 | | [SMP_T_IPV4] = "ipv4", |
63 | | [SMP_T_IPV6] = "ipv6", |
64 | | [SMP_T_STR] = "str", |
65 | | [SMP_T_BIN] = "bin", |
66 | | [SMP_T_METH] = "meth", |
67 | | }; |
68 | | |
69 | | /* Returns SMP_T_* smp matching with <type> name or SMP_TYPES if |
70 | | * not found. |
71 | | */ |
72 | | int type_to_smp(const char *type) |
73 | 0 | { |
74 | 0 | int it = 0; |
75 | |
|
76 | 0 | while (it < SMP_TYPES) { |
77 | 0 | if (strcmp(type, smp_to_type[it]) == 0) |
78 | 0 | break; // found |
79 | 0 | it += 1; |
80 | 0 | } |
81 | 0 | return it; |
82 | 0 | } |
83 | | |
84 | | /* static sample used in sample_process() when <p> is NULL */ |
85 | | static THREAD_LOCAL struct sample temp_smp; |
86 | | |
87 | | /* list head of all known sample fetch keywords */ |
88 | | static struct sample_fetch_kw_list sample_fetches = { |
89 | | .list = LIST_HEAD_INIT(sample_fetches.list) |
90 | | }; |
91 | | |
92 | | /* list head of all known sample format conversion keywords */ |
93 | | static struct sample_conv_kw_list sample_convs = { |
94 | | .list = LIST_HEAD_INIT(sample_convs.list) |
95 | | }; |
96 | | |
97 | | const unsigned int fetch_cap[SMP_SRC_ENTRIES] = { |
98 | | [SMP_SRC_CONST] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT | |
99 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
100 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
101 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
102 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
103 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
104 | | SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL_CFG_PARSER | |
105 | | SMP_VAL_CLI_PARSER ), |
106 | | |
107 | | [SMP_SRC_INTRN] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT | |
108 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
109 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
110 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
111 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
112 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
113 | | SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
114 | | SMP_VAL_CLI_PARSER ), |
115 | | |
116 | | [SMP_SRC_LISTN] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT | |
117 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
118 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
119 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
120 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
121 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
122 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
123 | | SMP_VAL___________ ), |
124 | | |
125 | | [SMP_SRC_FTEND] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT | |
126 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
127 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
128 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
129 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
130 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
131 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
132 | | SMP_VAL___________ ), |
133 | | |
134 | | [SMP_SRC_L4CLI] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT | |
135 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
136 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
137 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
138 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
139 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
140 | | SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
141 | | SMP_VAL___________ ), |
142 | | |
143 | | [SMP_SRC_L5CLI] = (SMP_VAL___________ | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT | |
144 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
145 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
146 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
147 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
148 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
149 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
150 | | SMP_VAL___________ ), |
151 | | |
152 | | [SMP_SRC_TRACK] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT | |
153 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
154 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
155 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
156 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
157 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
158 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
159 | | SMP_VAL___________ ), |
160 | | |
161 | | [SMP_SRC_L6REQ] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT | |
162 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
163 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
164 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ | |
165 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
166 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
167 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
168 | | SMP_VAL___________ ), |
169 | | |
170 | | [SMP_SRC_HRQHV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT | |
171 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
172 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
173 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ | |
174 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
175 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
176 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
177 | | SMP_VAL___________ ), |
178 | | |
179 | | [SMP_SRC_HRQHP] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT | |
180 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
181 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
182 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
183 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
184 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
185 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
186 | | SMP_VAL___________ ), |
187 | | |
188 | | [SMP_SRC_HRQBO] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
189 | | SMP_VAL___________ | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
190 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
191 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ | |
192 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
193 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
194 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
195 | | SMP_VAL___________ ), |
196 | | |
197 | | [SMP_SRC_BKEND] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
198 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
199 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
200 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
201 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
202 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
203 | | SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
204 | | SMP_VAL___________ ), |
205 | | |
206 | | [SMP_SRC_SERVR] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
207 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
208 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
209 | | SMP_VAL___________ | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
210 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
211 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
212 | | SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
213 | | SMP_VAL___________ ), |
214 | | |
215 | | [SMP_SRC_L4SRV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
216 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
217 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
218 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT | |
219 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
220 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
221 | | SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
222 | | SMP_VAL___________ ), |
223 | | |
224 | | [SMP_SRC_L5SRV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
225 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
226 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
227 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT | |
228 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
229 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
230 | | SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
231 | | SMP_VAL___________ ), |
232 | | |
233 | | [SMP_SRC_L6RES] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
234 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
235 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
236 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT | |
237 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
238 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
239 | | SMP_VAL___________ | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
240 | | SMP_VAL___________ ), |
241 | | |
242 | | [SMP_SRC_HRSHV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
243 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
244 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
245 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT | |
246 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
247 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
248 | | SMP_VAL___________ | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
249 | | SMP_VAL___________ ), |
250 | | |
251 | | [SMP_SRC_HRSHP] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
252 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
253 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
254 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT | |
255 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
256 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
257 | | SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
258 | | SMP_VAL___________ ), |
259 | | |
260 | | [SMP_SRC_HRSBO] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
261 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
262 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
263 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
264 | | SMP_VAL___________ | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
265 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
266 | | SMP_VAL___________ | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ | |
267 | | SMP_VAL___________ ), |
268 | | |
269 | | [SMP_SRC_RQFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT | |
270 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
271 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
272 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
273 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
274 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
275 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
276 | | SMP_VAL___________ ), |
277 | | |
278 | | [SMP_SRC_RSFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
279 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
280 | | SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ | |
281 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
282 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
283 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
284 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
285 | | SMP_VAL___________ ), |
286 | | |
287 | | [SMP_SRC_TXFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT | |
288 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
289 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
290 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
291 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
292 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
293 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
294 | | SMP_VAL___________ ), |
295 | | |
296 | | [SMP_SRC_SSFIN] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT | |
297 | | SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK | |
298 | | SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY | |
299 | | SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | |
300 | | SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | |
301 | | SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | |
302 | | SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ | |
303 | | SMP_VAL___________ ), |
304 | | }; |
305 | | |
306 | | static const char *fetch_src_names[SMP_SRC_ENTRIES] = { |
307 | | [SMP_SRC_INTRN] = "internal state", |
308 | | [SMP_SRC_LISTN] = "listener", |
309 | | [SMP_SRC_FTEND] = "frontend", |
310 | | [SMP_SRC_L4CLI] = "client address", |
311 | | [SMP_SRC_L5CLI] = "client-side connection", |
312 | | [SMP_SRC_TRACK] = "track counters", |
313 | | [SMP_SRC_L6REQ] = "request buffer", |
314 | | [SMP_SRC_HRQHV] = "HTTP request headers", |
315 | | [SMP_SRC_HRQHP] = "HTTP request", |
316 | | [SMP_SRC_HRQBO] = "HTTP request body", |
317 | | [SMP_SRC_BKEND] = "backend", |
318 | | [SMP_SRC_SERVR] = "server", |
319 | | [SMP_SRC_L4SRV] = "server address", |
320 | | [SMP_SRC_L5SRV] = "server-side connection", |
321 | | [SMP_SRC_L6RES] = "response buffer", |
322 | | [SMP_SRC_HRSHV] = "HTTP response headers", |
323 | | [SMP_SRC_HRSHP] = "HTTP response", |
324 | | [SMP_SRC_HRSBO] = "HTTP response body", |
325 | | [SMP_SRC_RQFIN] = "request buffer statistics", |
326 | | [SMP_SRC_RSFIN] = "response buffer statistics", |
327 | | [SMP_SRC_TXFIN] = "transaction statistics", |
328 | | [SMP_SRC_SSFIN] = "session statistics", |
329 | | }; |
330 | | |
331 | | static const char *fetch_ckp_names[SMP_CKP_ENTRIES] = { |
332 | | [SMP_CKP_FE_CON_ACC] = "frontend tcp-request connection rule", |
333 | | [SMP_CKP_FE_SES_ACC] = "frontend tcp-request session rule", |
334 | | [SMP_CKP_FE_REQ_CNT] = "frontend tcp-request content rule", |
335 | | [SMP_CKP_FE_HRQ_HDR] = "frontend http-request header rule", |
336 | | [SMP_CKP_FE_HRQ_BDY] = "frontend http-request body rule", |
337 | | [SMP_CKP_FE_SET_BCK] = "frontend use-backend rule", |
338 | | [SMP_CKP_BE_REQ_CNT] = "backend tcp-request content rule", |
339 | | [SMP_CKP_BE_HRQ_HDR] = "backend http-request header rule", |
340 | | [SMP_CKP_BE_HRQ_BDY] = "backend http-request body rule", |
341 | | [SMP_CKP_BE_SET_SRV] = "backend use-server, balance or stick-match rule", |
342 | | [SMP_CKP_BE_SRV_CON] = "server source selection", |
343 | | [SMP_CKP_BE_RES_CNT] = "backend tcp-response content rule", |
344 | | [SMP_CKP_BE_HRS_HDR] = "backend http-response header rule", |
345 | | [SMP_CKP_BE_HRS_BDY] = "backend http-response body rule", |
346 | | [SMP_CKP_BE_STO_RUL] = "backend stick-store rule", |
347 | | [SMP_CKP_FE_RES_CNT] = "frontend tcp-response content rule", |
348 | | [SMP_CKP_FE_HRS_HDR] = "frontend http-response header rule", |
349 | | [SMP_CKP_FE_HRS_BDY] = "frontend http-response body rule", |
350 | | [SMP_CKP_FE_LOG_END] = "logs", |
351 | | [SMP_CKP_BE_CHK_RUL] = "backend tcp-check rule", |
352 | | [SMP_CKP_CFG_PARSER] = "configuration parser", |
353 | | [SMP_CKP_CLI_PARSER] = "CLI parser", |
354 | | }; |
355 | | |
356 | | /* This function returns the most accurate expected type of the data returned |
357 | | * by the sample_expr. It assumes that the <expr> and all of its converters are |
358 | | * properly initialized. |
359 | | */ |
360 | | int smp_expr_output_type(struct sample_expr *expr) |
361 | 0 | { |
362 | 0 | struct sample_conv_expr *cur_smp = NULL; |
363 | 0 | int cur_type = SMP_T_ANY; /* current type in the chain */ |
364 | 0 | int next_type = SMP_T_ANY; /* next type in the chain */ |
365 | |
|
366 | 0 | if (!LIST_ISEMPTY(&expr->conv_exprs)) { |
367 | | /* Ignore converters that output SMP_T_SAME if switching to them is |
368 | | * conversion-free. (such converter's output match with input, thus only |
369 | | * their input is considered) |
370 | | * |
371 | | * We start looking at the end of conv list and then loop back until the |
372 | | * sample fetch for better performance (it is more likely to find the last |
373 | | * effective output type near the end of the chain) |
374 | | */ |
375 | 0 | do { |
376 | 0 | struct list *cur_head = (cur_smp) ? &cur_smp->list : &expr->conv_exprs; |
377 | |
|
378 | 0 | cur_smp = LIST_PREV(cur_head, struct sample_conv_expr *, list); |
379 | 0 | if (cur_smp->conv->out_type != SMP_T_SAME) { |
380 | | /* current converter has effective out_type */ |
381 | 0 | cur_type = cur_smp->conv->out_type; |
382 | 0 | goto out; |
383 | 0 | } |
384 | 0 | else if (sample_casts[cur_type][next_type] != c_none) |
385 | 0 | return next_type; /* switching to next type is not conversion-free */ |
386 | | |
387 | 0 | next_type = cur_smp->conv->in_type; |
388 | 0 | } while (cur_smp != LIST_NEXT(&expr->conv_exprs, struct sample_conv_expr *, list)); |
389 | 0 | } |
390 | | /* conv list empty or doesn't have effective out_type, |
391 | | * falling back to sample fetch out_type |
392 | | */ |
393 | 0 | cur_type = expr->fetch->out_type; |
394 | 0 | out: |
395 | 0 | if (sample_casts[cur_type][next_type] != c_none) |
396 | 0 | return next_type; /* switching to next type is not conversion-free */ |
397 | 0 | return cur_type; |
398 | 0 | } |
399 | | |
400 | | |
401 | | /* fill the trash with a comma-delimited list of source names for the <use> bit |
402 | | * field which must be composed of a non-null set of SMP_USE_* flags. The return |
403 | | * value is the pointer to the string in the trash buffer. |
404 | | */ |
405 | | const char *sample_src_names(unsigned int use) |
406 | 0 | { |
407 | 0 | int bit; |
408 | |
|
409 | 0 | trash.data = 0; |
410 | 0 | trash.area[0] = '\0'; |
411 | 0 | for (bit = 0; bit < SMP_SRC_ENTRIES; bit++) { |
412 | 0 | if (!(use & ~((1 << bit) - 1))) |
413 | 0 | break; /* no more bits */ |
414 | | |
415 | 0 | if (!(use & (1 << bit))) |
416 | 0 | continue; /* bit not set */ |
417 | | |
418 | 0 | trash.data += snprintf(trash.area + trash.data, |
419 | 0 | trash.size - trash.data, "%s%s", |
420 | 0 | (use & ((1 << bit) - 1)) ? "," : "", |
421 | 0 | fetch_src_names[bit]); |
422 | 0 | } |
423 | 0 | return trash.area; |
424 | 0 | } |
425 | | |
426 | | /* return a pointer to the correct sample checkpoint name, or "unknown" when |
427 | | * the flags are invalid. Only the lowest bit is used, higher bits are ignored |
428 | | * if set. |
429 | | */ |
430 | | const char *sample_ckp_names(unsigned int use) |
431 | 0 | { |
432 | 0 | int bit; |
433 | |
|
434 | 0 | for (bit = 0; bit < SMP_CKP_ENTRIES; bit++) |
435 | 0 | if (use & (1 << bit)) |
436 | 0 | return fetch_ckp_names[bit]; |
437 | 0 | return "unknown sample check place, please report this bug"; |
438 | 0 | } |
439 | | |
440 | | /* |
441 | | * Registers the sample fetch keyword list <kwl> as a list of valid keywords |
442 | | * for next parsing sessions. The fetch keywords capabilities are also computed |
443 | | * from their ->use field. |
444 | | */ |
445 | | void sample_register_fetches(struct sample_fetch_kw_list *kwl) |
446 | 0 | { |
447 | 0 | struct sample_fetch *sf; |
448 | 0 | int bit; |
449 | |
|
450 | 0 | for (sf = kwl->kw; sf->kw != NULL; sf++) { |
451 | 0 | for (bit = 0; bit < SMP_SRC_ENTRIES; bit++) |
452 | 0 | if (sf->use & (1 << bit)) |
453 | 0 | sf->val |= fetch_cap[bit]; |
454 | | /* store declaration file/line if known */ |
455 | 0 | if (sf->exec_ctx.type) |
456 | 0 | continue; |
457 | | |
458 | 0 | if (caller_initcall) { |
459 | 0 | sf->exec_ctx.type = TH_EX_CTX_INITCALL; |
460 | 0 | sf->exec_ctx.initcall = caller_initcall; |
461 | 0 | } else { |
462 | 0 | sf->exec_ctx.type = TH_EX_CTX_SMPF; |
463 | 0 | sf->exec_ctx.smpf_kwl = kwl; |
464 | 0 | } |
465 | 0 | } |
466 | 0 | LIST_APPEND(&sample_fetches.list, &kwl->list); |
467 | 0 | } |
468 | | |
469 | | /* |
470 | | * Registers the sample format coverstion keyword list <pckl> as a list of valid keywords for next |
471 | | * parsing sessions. |
472 | | */ |
473 | | void sample_register_convs(struct sample_conv_kw_list *pckl) |
474 | 0 | { |
475 | 0 | struct sample_conv *sc; |
476 | | |
477 | | /* store declaration file/line if known */ |
478 | 0 | for (sc = pckl->kw; sc->kw != NULL; sc++) { |
479 | 0 | if (sc->exec_ctx.type) |
480 | 0 | continue; |
481 | | |
482 | 0 | if (caller_initcall) { |
483 | 0 | sc->exec_ctx.type = TH_EX_CTX_INITCALL; |
484 | 0 | sc->exec_ctx.initcall = caller_initcall; |
485 | 0 | } else { |
486 | 0 | sc->exec_ctx.type = TH_EX_CTX_CONV; |
487 | 0 | sc->exec_ctx.conv_kwl = pckl; |
488 | 0 | } |
489 | 0 | } |
490 | 0 | LIST_APPEND(&sample_convs.list, &pckl->list); |
491 | 0 | } |
492 | | |
493 | | /* |
494 | | * Returns the pointer on sample fetch keyword structure identified by |
495 | | * string of <len> in buffer <kw>. |
496 | | * |
497 | | */ |
498 | | struct sample_fetch *find_sample_fetch(const char *kw, int len) |
499 | 0 | { |
500 | 0 | int index; |
501 | 0 | struct sample_fetch_kw_list *kwl; |
502 | |
|
503 | 0 | list_for_each_entry(kwl, &sample_fetches.list, list) { |
504 | 0 | for (index = 0; kwl->kw[index].kw != NULL; index++) { |
505 | 0 | if (strncmp(kwl->kw[index].kw, kw, len) == 0 && |
506 | 0 | kwl->kw[index].kw[len] == '\0') |
507 | 0 | return &kwl->kw[index]; |
508 | 0 | } |
509 | 0 | } |
510 | 0 | return NULL; |
511 | 0 | } |
512 | | |
513 | | /* dump list of registered sample fetch keywords on stdout */ |
514 | | void smp_dump_fetch_kw(void) |
515 | 0 | { |
516 | 0 | struct sample_fetch_kw_list *kwl; |
517 | 0 | struct sample_fetch *kwp, *kw; |
518 | 0 | uint64_t mask; |
519 | 0 | int index; |
520 | 0 | int arg; |
521 | 0 | int bit; |
522 | |
|
523 | 0 | for (bit = 0; bit <= SMP_CKP_ENTRIES + 1; bit++) { |
524 | 0 | putchar('#'); |
525 | 0 | for (index = 0; bit + index <= SMP_CKP_ENTRIES; index++) |
526 | 0 | putchar(' '); |
527 | 0 | for (index = 0; index < bit && index < SMP_CKP_ENTRIES; index++) |
528 | 0 | printf((bit <= SMP_CKP_ENTRIES) ? "/ " : " |"); |
529 | 0 | for (index = bit; bit < SMP_CKP_ENTRIES && index < SMP_CKP_ENTRIES + 2; index++) |
530 | 0 | if (index == bit) |
531 | 0 | putchar('_'); |
532 | 0 | else if (index == bit + 1) |
533 | 0 | putchar('.'); |
534 | 0 | else |
535 | 0 | putchar('-'); |
536 | 0 | printf(" %s\n", (bit < SMP_CKP_ENTRIES) ? fetch_ckp_names[bit] : ""); |
537 | 0 | } |
538 | |
|
539 | 0 | for (kw = kwp = NULL;; kwp = kw) { |
540 | 0 | list_for_each_entry(kwl, &sample_fetches.list, list) { |
541 | 0 | for (index = 0; kwl->kw[index].kw != NULL; index++) { |
542 | 0 | if (strordered(kwp ? kwp->kw : NULL, |
543 | 0 | kwl->kw[index].kw, |
544 | 0 | kw != kwp ? kw->kw : NULL)) |
545 | 0 | kw = &kwl->kw[index]; |
546 | 0 | } |
547 | 0 | } |
548 | |
|
549 | 0 | if (kw == kwp) |
550 | 0 | break; |
551 | | |
552 | 0 | printf("[ "); |
553 | 0 | for (bit = 0; bit < SMP_CKP_ENTRIES; bit++) |
554 | 0 | printf("%s", (kw->val & (1 << bit)) ? "Y " : ". "); |
555 | |
|
556 | 0 | printf("] %s", kw->kw); |
557 | 0 | if (kw->arg_mask) { |
558 | 0 | mask = kw->arg_mask >> ARGM_BITS; |
559 | 0 | printf("("); |
560 | 0 | for (arg = 0; |
561 | 0 | arg < ARGM_NBARGS && ((mask >> (arg * ARGT_BITS)) & ARGT_MASK); |
562 | 0 | arg++) { |
563 | 0 | if (arg == (kw->arg_mask & ARGM_MASK)) { |
564 | | /* now dumping extra args */ |
565 | 0 | printf("["); |
566 | 0 | } |
567 | 0 | if (arg) |
568 | 0 | printf(","); |
569 | 0 | printf("%s", arg_type_names[(mask >> (arg * ARGT_BITS)) & ARGT_MASK]); |
570 | 0 | } |
571 | 0 | if (arg > (kw->arg_mask & ARGM_MASK)) { |
572 | | /* extra args were dumped */ |
573 | 0 | printf("]"); |
574 | 0 | } |
575 | 0 | printf(")"); |
576 | 0 | } |
577 | 0 | printf(": %s", smp_to_type[kw->out_type]); |
578 | 0 | printf("\n"); |
579 | 0 | } |
580 | 0 | } |
581 | | |
582 | | /* dump list of registered sample converter keywords on stdout */ |
583 | | void smp_dump_conv_kw(void) |
584 | 0 | { |
585 | 0 | struct sample_conv_kw_list *kwl; |
586 | 0 | struct sample_conv *kwp, *kw; |
587 | 0 | uint64_t mask; |
588 | 0 | int index; |
589 | 0 | int arg; |
590 | |
|
591 | 0 | for (kw = kwp = NULL;; kwp = kw) { |
592 | 0 | list_for_each_entry(kwl, &sample_convs.list, list) { |
593 | 0 | for (index = 0; kwl->kw[index].kw != NULL; index++) { |
594 | 0 | if (strordered(kwp ? kwp->kw : NULL, |
595 | 0 | kwl->kw[index].kw, |
596 | 0 | kw != kwp ? kw->kw : NULL)) |
597 | 0 | kw = &kwl->kw[index]; |
598 | 0 | } |
599 | 0 | } |
600 | |
|
601 | 0 | if (kw == kwp) |
602 | 0 | break; |
603 | | |
604 | 0 | printf("%s", kw->kw); |
605 | 0 | if (kw->arg_mask) { |
606 | 0 | mask = kw->arg_mask >> ARGM_BITS; |
607 | 0 | printf("("); |
608 | 0 | for (arg = 0; |
609 | 0 | arg < ARGM_NBARGS && ((mask >> (arg * ARGT_BITS)) & ARGT_MASK); |
610 | 0 | arg++) { |
611 | 0 | if (arg == (kw->arg_mask & ARGM_MASK)) { |
612 | | /* now dumping extra args */ |
613 | 0 | printf("["); |
614 | 0 | } |
615 | 0 | if (arg) |
616 | 0 | printf(","); |
617 | 0 | printf("%s", arg_type_names[(mask >> (arg * ARGT_BITS)) & ARGT_MASK]); |
618 | 0 | } |
619 | 0 | if (arg > (kw->arg_mask & ARGM_MASK)) { |
620 | | /* extra args were dumped */ |
621 | 0 | printf("]"); |
622 | 0 | } |
623 | 0 | printf(")"); |
624 | 0 | } |
625 | 0 | printf(": %s => %s", smp_to_type[kw->out_type], smp_to_type[kw->in_type]); |
626 | 0 | printf("\n"); |
627 | 0 | } |
628 | 0 | } |
629 | | |
630 | | /* This function browses the list of available sample fetches. <current> is |
631 | | * the last used sample fetch. If it is the first call, it must set to NULL. |
632 | | * <idx> is the index of the next sample fetch entry. It is used as private |
633 | | * value. It is useless to initiate it. |
634 | | * |
635 | | * It returns always the new fetch_sample entry, and NULL when the end of |
636 | | * the list is reached. |
637 | | */ |
638 | | struct sample_fetch *sample_fetch_getnext(struct sample_fetch *current, int *idx) |
639 | 0 | { |
640 | 0 | struct sample_fetch_kw_list *kwl; |
641 | 0 | struct sample_fetch *base; |
642 | |
|
643 | 0 | if (!current) { |
644 | | /* Get first kwl entry. */ |
645 | 0 | kwl = LIST_NEXT(&sample_fetches.list, struct sample_fetch_kw_list *, list); |
646 | 0 | (*idx) = 0; |
647 | 0 | } else { |
648 | | /* Get kwl corresponding to the current entry. */ |
649 | 0 | base = current + 1 - (*idx); |
650 | 0 | kwl = container_of(base, struct sample_fetch_kw_list, kw); |
651 | 0 | } |
652 | |
|
653 | 0 | while (1) { |
654 | | |
655 | | /* Check if kwl is the last entry. */ |
656 | 0 | if (&kwl->list == &sample_fetches.list) |
657 | 0 | return NULL; |
658 | | |
659 | | /* idx contain the next keyword. If it is available, return it. */ |
660 | 0 | if (kwl->kw[*idx].kw) { |
661 | 0 | (*idx)++; |
662 | 0 | return &kwl->kw[(*idx)-1]; |
663 | 0 | } |
664 | | |
665 | | /* get next entry in the main list, and return NULL if the end is reached. */ |
666 | 0 | kwl = LIST_NEXT(&kwl->list, struct sample_fetch_kw_list *, list); |
667 | | |
668 | | /* Set index to 0, ans do one other loop. */ |
669 | 0 | (*idx) = 0; |
670 | 0 | } |
671 | 0 | } |
672 | | |
673 | | /* This function browses the list of available converters. <current> is |
674 | | * the last used converter. If it is the first call, it must set to NULL. |
675 | | * <idx> is the index of the next converter entry. It is used as private |
676 | | * value. It is useless to initiate it. |
677 | | * |
678 | | * It returns always the next sample_conv entry, and NULL when the end of |
679 | | * the list is reached. |
680 | | */ |
681 | | struct sample_conv *sample_conv_getnext(struct sample_conv *current, int *idx) |
682 | 0 | { |
683 | 0 | struct sample_conv_kw_list *kwl; |
684 | 0 | struct sample_conv *base; |
685 | |
|
686 | 0 | if (!current) { |
687 | | /* Get first kwl entry. */ |
688 | 0 | kwl = LIST_NEXT(&sample_convs.list, struct sample_conv_kw_list *, list); |
689 | 0 | (*idx) = 0; |
690 | 0 | } else { |
691 | | /* Get kwl corresponding to the current entry. */ |
692 | 0 | base = current + 1 - (*idx); |
693 | 0 | kwl = container_of(base, struct sample_conv_kw_list, kw); |
694 | 0 | } |
695 | |
|
696 | 0 | while (1) { |
697 | | /* Check if kwl is the last entry. */ |
698 | 0 | if (&kwl->list == &sample_convs.list) |
699 | 0 | return NULL; |
700 | | |
701 | | /* idx contain the next keyword. If it is available, return it. */ |
702 | 0 | if (kwl->kw[*idx].kw) { |
703 | 0 | (*idx)++; |
704 | 0 | return &kwl->kw[(*idx)-1]; |
705 | 0 | } |
706 | | |
707 | | /* get next entry in the main list, and return NULL if the end is reached. */ |
708 | 0 | kwl = LIST_NEXT(&kwl->list, struct sample_conv_kw_list *, list); |
709 | | |
710 | | /* Set index to 0, ans do one other loop. */ |
711 | 0 | (*idx) = 0; |
712 | 0 | } |
713 | 0 | } |
714 | | |
715 | | /* |
716 | | * Returns the pointer on sample format conversion keyword structure identified by |
717 | | * string of <len> in buffer <kw>. |
718 | | * |
719 | | */ |
720 | | struct sample_conv *find_sample_conv(const char *kw, int len) |
721 | 0 | { |
722 | 0 | int index; |
723 | 0 | struct sample_conv_kw_list *kwl; |
724 | |
|
725 | 0 | list_for_each_entry(kwl, &sample_convs.list, list) { |
726 | 0 | for (index = 0; kwl->kw[index].kw != NULL; index++) { |
727 | 0 | if (strncmp(kwl->kw[index].kw, kw, len) == 0 && |
728 | 0 | kwl->kw[index].kw[len] == '\0') |
729 | 0 | return &kwl->kw[index]; |
730 | 0 | } |
731 | 0 | } |
732 | 0 | return NULL; |
733 | 0 | } |
734 | | |
735 | | /******************************************************************/ |
736 | | /* Sample casts functions */ |
737 | | /******************************************************************/ |
738 | | |
739 | | static int c_ip2int(struct sample *smp) |
740 | 0 | { |
741 | 0 | smp->data.u.sint = ntohl(smp->data.u.ipv4.s_addr); |
742 | 0 | smp->data.type = SMP_T_SINT; |
743 | 0 | return 1; |
744 | 0 | } |
745 | | |
746 | | static int c_ip2str(struct sample *smp) |
747 | 0 | { |
748 | 0 | struct buffer *trash = get_trash_chunk(); |
749 | |
|
750 | 0 | if (!inet_ntop(AF_INET, (void *)&smp->data.u.ipv4, trash->area, trash->size)) |
751 | 0 | return 0; |
752 | | |
753 | 0 | trash->data = strlen(trash->area); |
754 | 0 | smp->data.u.str = *trash; |
755 | 0 | smp->data.type = SMP_T_STR; |
756 | 0 | smp->flags &= ~SMP_F_CONST; |
757 | |
|
758 | 0 | return 1; |
759 | 0 | } |
760 | | |
761 | | static int c_ip2ipv6(struct sample *smp) |
762 | 0 | { |
763 | 0 | v4tov6(&smp->data.u.ipv6, &smp->data.u.ipv4); |
764 | 0 | smp->data.type = SMP_T_IPV6; |
765 | 0 | return 1; |
766 | 0 | } |
767 | | |
768 | | static int c_ipv62ip(struct sample *smp) |
769 | 0 | { |
770 | 0 | if (!v6tov4(&smp->data.u.ipv4, &smp->data.u.ipv6)) |
771 | 0 | return 0; |
772 | 0 | smp->data.type = SMP_T_IPV4; |
773 | 0 | return 1; |
774 | 0 | } |
775 | | |
776 | | static int c_ipv62str(struct sample *smp) |
777 | 0 | { |
778 | 0 | struct buffer *trash = get_trash_chunk(); |
779 | |
|
780 | 0 | if (!inet_ntop(AF_INET6, (void *)&smp->data.u.ipv6, trash->area, trash->size)) |
781 | 0 | return 0; |
782 | | |
783 | 0 | trash->data = strlen(trash->area); |
784 | 0 | smp->data.u.str = *trash; |
785 | 0 | smp->data.type = SMP_T_STR; |
786 | 0 | smp->flags &= ~SMP_F_CONST; |
787 | 0 | return 1; |
788 | 0 | } |
789 | | |
790 | | /* |
791 | | static int c_ipv62ip(struct sample *smp) |
792 | | { |
793 | | return v6tov4(&smp->data.u.ipv4, &smp->data.u.ipv6); |
794 | | } |
795 | | */ |
796 | | |
797 | | static int c_int2ip(struct sample *smp) |
798 | 0 | { |
799 | 0 | smp->data.u.ipv4.s_addr = htonl((unsigned int)smp->data.u.sint); |
800 | 0 | smp->data.type = SMP_T_IPV4; |
801 | 0 | return 1; |
802 | 0 | } |
803 | | |
804 | | static int c_int2ipv6(struct sample *smp) |
805 | 0 | { |
806 | 0 | smp->data.u.ipv4.s_addr = htonl((unsigned int)smp->data.u.sint); |
807 | 0 | v4tov6(&smp->data.u.ipv6, &smp->data.u.ipv4); |
808 | 0 | smp->data.type = SMP_T_IPV6; |
809 | 0 | return 1; |
810 | 0 | } |
811 | | |
812 | | static int c_str2addr(struct sample *smp) |
813 | 0 | { |
814 | 0 | if (!buf2ip(smp->data.u.str.area, smp->data.u.str.data, &smp->data.u.ipv4)) { |
815 | 0 | if (!buf2ip6(smp->data.u.str.area, smp->data.u.str.data, &smp->data.u.ipv6)) |
816 | 0 | return 0; |
817 | 0 | smp->data.type = SMP_T_IPV6; |
818 | 0 | smp->flags &= ~SMP_F_CONST; |
819 | 0 | return 1; |
820 | 0 | } |
821 | 0 | smp->data.type = SMP_T_IPV4; |
822 | 0 | smp->flags &= ~SMP_F_CONST; |
823 | 0 | return 1; |
824 | 0 | } |
825 | | |
826 | | static int c_str2ip(struct sample *smp) |
827 | 0 | { |
828 | 0 | if (!buf2ip(smp->data.u.str.area, smp->data.u.str.data, &smp->data.u.ipv4)) |
829 | 0 | return 0; |
830 | 0 | smp->data.type = SMP_T_IPV4; |
831 | 0 | smp->flags &= ~SMP_F_CONST; |
832 | 0 | return 1; |
833 | 0 | } |
834 | | |
835 | | static int c_str2ipv6(struct sample *smp) |
836 | 0 | { |
837 | 0 | if (!buf2ip6(smp->data.u.str.area, smp->data.u.str.data, &smp->data.u.ipv6)) |
838 | 0 | return 0; |
839 | 0 | smp->data.type = SMP_T_IPV6; |
840 | 0 | smp->flags &= ~SMP_F_CONST; |
841 | 0 | return 1; |
842 | 0 | } |
843 | | |
844 | | /* |
845 | | * The NULL char always enforces the end of string if it is met. |
846 | | * Data is never changed, so we can ignore the CONST case |
847 | | */ |
848 | | static int c_bin2str(struct sample *smp) |
849 | 0 | { |
850 | 0 | int i; |
851 | |
|
852 | 0 | for (i = 0; i < smp->data.u.str.data; i++) { |
853 | 0 | if (!smp->data.u.str.area[i]) { |
854 | 0 | smp->data.u.str.data = i; |
855 | 0 | break; |
856 | 0 | } |
857 | 0 | } |
858 | 0 | smp->data.type = SMP_T_STR; |
859 | 0 | return 1; |
860 | 0 | } |
861 | | |
862 | | static int c_int2str(struct sample *smp) |
863 | 0 | { |
864 | 0 | struct buffer *trash = get_trash_chunk(); |
865 | 0 | char *pos; |
866 | |
|
867 | 0 | pos = lltoa_r(smp->data.u.sint, trash->area, trash->size); |
868 | 0 | if (!pos) |
869 | 0 | return 0; |
870 | | |
871 | 0 | trash->size = trash->size - (pos - trash->area); |
872 | 0 | trash->area = pos; |
873 | 0 | trash->data = strlen(pos); |
874 | 0 | smp->data.u.str = *trash; |
875 | 0 | smp->data.type = SMP_T_STR; |
876 | 0 | smp->flags &= ~SMP_F_CONST; |
877 | 0 | return 1; |
878 | 0 | } |
879 | | |
880 | | /* This function unconditionally duplicates data and removes the "const" flag. |
881 | | * For strings and binary blocks, it also provides a known allocated size with |
882 | | * a length that is capped to the size, and ensures a trailing zero is always |
883 | | * appended for strings. This is necessary for some operations which may |
884 | | * require to extend the length. It returns 0 if it fails, 1 on success. |
885 | | */ |
886 | | int smp_dup(struct sample *smp) |
887 | 0 | { |
888 | 0 | struct buffer *trash, *buf; |
889 | |
|
890 | 0 | switch (smp->data.type) { |
891 | 0 | case SMP_T_BOOL: |
892 | 0 | case SMP_T_SINT: |
893 | 0 | case SMP_T_ADDR: |
894 | 0 | case SMP_T_IPV4: |
895 | 0 | case SMP_T_IPV6: |
896 | | /* These type are not const. */ |
897 | 0 | break; |
898 | | |
899 | 0 | case SMP_T_METH: |
900 | 0 | if (smp->data.u.meth.meth != HTTP_METH_OTHER) |
901 | 0 | break; |
902 | 0 | __fallthrough; |
903 | |
|
904 | 0 | case SMP_T_STR: |
905 | 0 | buf = (smp->data.type == SMP_T_STR ? &smp->data.u.str : &smp->data.u.meth.str); |
906 | 0 | trash = get_best_trash_chunk(buf, buf->data+1); |
907 | 0 | if (!trash) |
908 | 0 | return 0; |
909 | 0 | trash->data = buf->data; |
910 | 0 | if (trash->data > trash->size - 1) |
911 | 0 | trash->data = trash->size - 1; |
912 | |
|
913 | 0 | memcpy(trash->area, buf->area, trash->data); |
914 | 0 | trash->area[trash->data] = 0; |
915 | 0 | *buf = *trash; |
916 | 0 | break; |
917 | | |
918 | 0 | case SMP_T_BIN: |
919 | 0 | trash = get_trash_chunk_sz(smp->data.u.str.data); |
920 | 0 | if (!trash) |
921 | 0 | return 0; |
922 | 0 | trash->data = smp->data.u.str.data; |
923 | 0 | if (trash->data > trash->size) |
924 | 0 | trash->data = trash->size; |
925 | |
|
926 | 0 | memcpy(trash->area, smp->data.u.str.area, trash->data); |
927 | 0 | smp->data.u.str = *trash; |
928 | 0 | break; |
929 | | |
930 | 0 | default: |
931 | | /* Other cases are unexpected. */ |
932 | 0 | return 0; |
933 | 0 | } |
934 | | |
935 | | /* remove const flag */ |
936 | 0 | smp->flags &= ~SMP_F_CONST; |
937 | 0 | return 1; |
938 | 0 | } |
939 | | |
940 | | int c_none(struct sample *smp) |
941 | 0 | { |
942 | 0 | return 1; |
943 | 0 | } |
944 | | |
945 | | /* special converter function used by pseudo types in the compatibility matrix |
946 | | * to inform that the conversion is theoretically allowed at parsing time. |
947 | | * |
948 | | * However, being a pseudo type, it may not be emitted by fetches or converters |
949 | | * so this function should never be called. If this is the case, then it means |
950 | | * that a pseudo type has been used as a final output type at runtime, which is |
951 | | * considered as a bug and should be fixed. To help spot this kind of bug, the |
952 | | * process will crash in this case. |
953 | | */ |
954 | | int c_pseudo(struct sample *smp) |
955 | 0 | { |
956 | 0 | ABORT_NOW(); // die loudly |
957 | | /* never reached */ |
958 | 0 | return 0; |
959 | 0 | } |
960 | | |
961 | | static int c_str2int(struct sample *smp) |
962 | 0 | { |
963 | 0 | const char *str; |
964 | 0 | const char *end; |
965 | |
|
966 | 0 | if (smp->data.u.str.data == 0) |
967 | 0 | return 0; |
968 | | |
969 | 0 | str = smp->data.u.str.area; |
970 | 0 | end = smp->data.u.str.area + smp->data.u.str.data; |
971 | |
|
972 | 0 | smp->data.u.sint = read_int64(&str, end); |
973 | 0 | smp->data.type = SMP_T_SINT; |
974 | 0 | smp->flags &= ~SMP_F_CONST; |
975 | 0 | return 1; |
976 | 0 | } |
977 | | |
978 | | static int c_str2meth(struct sample *smp) |
979 | 0 | { |
980 | 0 | enum http_meth_t meth; |
981 | 0 | int len; |
982 | |
|
983 | 0 | meth = find_http_meth(smp->data.u.str.area, smp->data.u.str.data); |
984 | 0 | if (meth == HTTP_METH_OTHER) { |
985 | 0 | len = smp->data.u.str.data; |
986 | 0 | smp->data.u.meth.str.area = smp->data.u.str.area; |
987 | 0 | smp->data.u.meth.str.data = len; |
988 | 0 | } |
989 | 0 | else |
990 | 0 | smp->flags &= ~SMP_F_CONST; |
991 | 0 | smp->data.u.meth.meth = meth; |
992 | 0 | smp->data.type = SMP_T_METH; |
993 | 0 | return 1; |
994 | 0 | } |
995 | | |
996 | | static int c_meth2str(struct sample *smp) |
997 | 0 | { |
998 | 0 | int len; |
999 | 0 | enum http_meth_t meth; |
1000 | |
|
1001 | 0 | if (smp->data.u.meth.meth == HTTP_METH_OTHER) { |
1002 | | /* The method is unknown. Copy the original pointer. */ |
1003 | 0 | len = smp->data.u.meth.str.data; |
1004 | 0 | smp->data.u.str.area = smp->data.u.meth.str.area; |
1005 | 0 | smp->data.u.str.data = len; |
1006 | 0 | smp->data.type = SMP_T_STR; |
1007 | 0 | } |
1008 | 0 | else if (smp->data.u.meth.meth < HTTP_METH_OTHER) { |
1009 | | /* The method is known, copy the pointer containing the string. */ |
1010 | 0 | meth = smp->data.u.meth.meth; |
1011 | 0 | smp->data.u.str.area = http_known_methods[meth].ptr; |
1012 | 0 | smp->data.u.str.data = http_known_methods[meth].len; |
1013 | 0 | smp->flags |= SMP_F_CONST; |
1014 | 0 | smp->data.type = SMP_T_STR; |
1015 | 0 | } |
1016 | 0 | else { |
1017 | | /* Unknown method */ |
1018 | 0 | return 0; |
1019 | 0 | } |
1020 | 0 | return 1; |
1021 | 0 | } |
1022 | | |
1023 | | static int c_addr2bin(struct sample *smp) |
1024 | 0 | { |
1025 | 0 | struct buffer *chk = get_trash_chunk(); |
1026 | |
|
1027 | 0 | if (smp->data.type == SMP_T_IPV4) { |
1028 | 0 | chk->data = 4; |
1029 | 0 | memcpy(chk->area, &smp->data.u.ipv4, chk->data); |
1030 | 0 | } |
1031 | 0 | else if (smp->data.type == SMP_T_IPV6) { |
1032 | 0 | chk->data = 16; |
1033 | 0 | memcpy(chk->area, &smp->data.u.ipv6, chk->data); |
1034 | 0 | } |
1035 | 0 | else |
1036 | 0 | return 0; |
1037 | | |
1038 | 0 | smp->data.u.str = *chk; |
1039 | 0 | smp->data.type = SMP_T_BIN; |
1040 | 0 | return 1; |
1041 | 0 | } |
1042 | | |
1043 | | static int c_int2bin(struct sample *smp) |
1044 | 0 | { |
1045 | 0 | struct buffer *chk = get_trash_chunk(); |
1046 | |
|
1047 | 0 | *(unsigned long long int *) chk->area = my_htonll(smp->data.u.sint); |
1048 | 0 | chk->data = 8; |
1049 | |
|
1050 | 0 | smp->data.u.str = *chk; |
1051 | 0 | smp->data.type = SMP_T_BIN; |
1052 | 0 | return 1; |
1053 | 0 | } |
1054 | | |
1055 | | static int c_bool2bin(struct sample *smp) |
1056 | 0 | { |
1057 | 0 | struct buffer *chk = get_trash_chunk(); |
1058 | |
|
1059 | 0 | *(unsigned long long int *)chk->area = my_htonll(!!smp->data.u.sint); |
1060 | 0 | chk->data = 8; |
1061 | 0 | smp->data.u.str = *chk; |
1062 | 0 | smp->data.type = SMP_T_BIN; |
1063 | 0 | return 1; |
1064 | 0 | } |
1065 | | |
1066 | | |
1067 | | /*****************************************************************/ |
1068 | | /* Sample casts matrix: */ |
1069 | | /* sample_casts[from type][to type] */ |
1070 | | /* NULL pointer used for impossible sample casts */ |
1071 | | /*****************************************************************/ |
1072 | | |
1073 | | sample_cast_fct sample_casts[SMP_TYPES][SMP_TYPES] = { |
1074 | | /* to: ANY SAME BOOL SINT ADDR IPV4 IPV6 STR BIN METH */ |
1075 | | /* from: ANY */ { c_none, NULL, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo }, |
1076 | | /* SAME */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, |
1077 | | /* BOOL */ { c_none, NULL, c_none, c_none, NULL, NULL, NULL, c_int2str, c_bool2bin, NULL }, |
1078 | | /* SINT */ { c_none, NULL, c_none, c_none, c_int2ip, c_int2ip, c_int2ipv6, c_int2str, c_int2bin, NULL }, |
1079 | | /* ADDR */ { c_none, NULL, NULL, NULL, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo, NULL }, |
1080 | | /* IPV4 */ { c_none, NULL, NULL, c_ip2int, c_none, c_none, c_ip2ipv6, c_ip2str, c_addr2bin, NULL }, |
1081 | | /* IPV6 */ { c_none, NULL, NULL, NULL, c_none, c_ipv62ip, c_none, c_ipv62str, c_addr2bin, NULL }, |
1082 | | /* STR */ { c_none, NULL, c_str2int, c_str2int, c_str2addr, c_str2ip, c_str2ipv6, c_none, c_none, c_str2meth }, |
1083 | | /* BIN */ { c_none, NULL, NULL, NULL, NULL, NULL, NULL, c_bin2str, c_none, c_str2meth }, |
1084 | | /* METH */ { c_none, NULL, NULL, NULL, NULL, NULL, NULL, c_meth2str, c_meth2str, c_none } |
1085 | | }; |
1086 | | |
1087 | | /* Process the converters (if any) for a sample expr after the first fetch |
1088 | | * keyword. We have two supported syntaxes for the converters, which can be |
1089 | | * combined: |
1090 | | * - comma-delimited list of converters just after the keyword and args ; |
1091 | | * - one converter per keyword (if <idx> != NULL) |
1092 | | * FIXME: should we continue to support this old syntax? |
1093 | | * The combination allows to have each keyword being a comma-delimited |
1094 | | * series of converters. |
1095 | | * |
1096 | | * We want to process the former first, then the latter. For this we start |
1097 | | * from the beginning of the supposed place in the exiting conv chain, which |
1098 | | * starts at the last comma (<start> which is then referred to as endt). |
1099 | | * |
1100 | | * If <endptr> is non-nul, it will be set to the first unparsed character |
1101 | | * (which may be the final '\0') on success. If it is nul, the expression |
1102 | | * must be properly terminated by a '\0' otherwise an error is reported. |
1103 | | * |
1104 | | * <expr> should point the the sample expression that is already initialized |
1105 | | * with the sample fetch that precedes the converters chain. |
1106 | | * |
1107 | | * The function returns a positive value for success and 0 for failure, in which |
1108 | | * case <err_msg> will point to an allocated string that brings some info |
1109 | | * about the failure. It is the caller's responsibility to free it. |
1110 | | */ |
1111 | | int sample_parse_expr_cnv(char **str, int *idx, char **endptr, char **err_msg, struct arg_list *al, const char *file, int line, |
1112 | | struct sample_expr *expr, const char *start) |
1113 | 0 | { |
1114 | 0 | struct sample_conv *conv; |
1115 | 0 | const char *endt = start; /* end of term */ |
1116 | 0 | const char *begw; /* beginning of word */ |
1117 | 0 | const char *endw; /* end of word */ |
1118 | 0 | char *ckw = NULL; |
1119 | 0 | unsigned long prev_type = expr->fetch->out_type; |
1120 | 0 | int success = 1; |
1121 | |
|
1122 | 0 | while (1) { |
1123 | 0 | struct sample_conv_expr *conv_expr; |
1124 | 0 | int err_arg; |
1125 | 0 | int argcnt; |
1126 | |
|
1127 | 0 | if (*endt && *endt != ',') { |
1128 | 0 | if (endptr) { |
1129 | | /* end found, let's stop here */ |
1130 | 0 | break; |
1131 | 0 | } |
1132 | 0 | if (ckw) |
1133 | 0 | memprintf(err_msg, "missing comma after converter '%s'", ckw); |
1134 | 0 | else |
1135 | 0 | memprintf(err_msg, "missing comma after fetch keyword"); |
1136 | 0 | goto out_error; |
1137 | 0 | } |
1138 | | |
1139 | | /* FIXME: how long should we support such idiocies ? Maybe we |
1140 | | * should already warn ? |
1141 | | */ |
1142 | 0 | while (*endt == ',') /* then trailing commas */ |
1143 | 0 | endt++; |
1144 | |
|
1145 | 0 | begw = endt; /* start of converter */ |
1146 | |
|
1147 | 0 | if (!*begw) { |
1148 | | /* none ? skip to next string if idx is set */ |
1149 | 0 | if (!idx) |
1150 | 0 | break; /* end of converters */ |
1151 | 0 | (*idx)++; |
1152 | 0 | begw = str[*idx]; |
1153 | 0 | if (!begw || !*begw) |
1154 | 0 | break; |
1155 | 0 | } |
1156 | | |
1157 | 0 | for (endw = begw; is_idchar(*endw); endw++) |
1158 | 0 | ; |
1159 | |
|
1160 | 0 | ha_free(&ckw); |
1161 | 0 | ckw = my_strndup(begw, endw - begw); |
1162 | |
|
1163 | 0 | conv = find_sample_conv(begw, endw - begw); |
1164 | 0 | if (!conv) { |
1165 | | /* we found an isolated keyword that we don't know, it's not ours */ |
1166 | 0 | if (idx && begw == str[*idx]) { |
1167 | 0 | endt = begw; |
1168 | 0 | break; |
1169 | 0 | } |
1170 | 0 | memprintf(err_msg, "unknown converter '%s'", ckw); |
1171 | 0 | goto out_error; |
1172 | 0 | } |
1173 | | |
1174 | 0 | if (conv->in_type >= SMP_TYPES || conv->out_type >= SMP_TYPES) { |
1175 | 0 | memprintf(err_msg, "return type of converter '%s' is unknown", ckw); |
1176 | 0 | goto out_error; |
1177 | 0 | } |
1178 | | |
1179 | | /* If impossible type conversion */ |
1180 | 0 | if (!sample_casts[prev_type][conv->in_type]) { |
1181 | 0 | memprintf(err_msg, "converter '%s' cannot be applied", ckw); |
1182 | 0 | goto out_error; |
1183 | 0 | } |
1184 | | |
1185 | | /* Ignore converters that output SMP_T_SAME if switching to them is |
1186 | | * conversion-free. (such converter's output match with input, thus only |
1187 | | * their input is considered) |
1188 | | */ |
1189 | 0 | if (conv->out_type != SMP_T_SAME) |
1190 | 0 | prev_type = conv->out_type; |
1191 | 0 | else if (sample_casts[prev_type][conv->in_type] != c_none) |
1192 | 0 | prev_type = conv->in_type; |
1193 | |
|
1194 | 0 | conv_expr = calloc(1, sizeof(*conv_expr)); |
1195 | 0 | if (!conv_expr) |
1196 | 0 | goto out_error; |
1197 | | |
1198 | 0 | LIST_APPEND(&(expr->conv_exprs), &(conv_expr->list)); |
1199 | 0 | conv_expr->conv = conv; |
1200 | |
|
1201 | 0 | if (al) { |
1202 | 0 | al->kw = expr->fetch->kw; |
1203 | 0 | al->conv = conv_expr->conv->kw; |
1204 | 0 | } |
1205 | 0 | argcnt = make_arg_list(endw, -1, conv->arg_mask, &conv_expr->arg_p, err_msg, &endt, &err_arg, al); |
1206 | 0 | if (argcnt < 0) { |
1207 | 0 | memprintf(err_msg, "invalid arg %d in converter '%s' : %s", err_arg+1, ckw, *err_msg); |
1208 | 0 | goto out_error; |
1209 | 0 | } |
1210 | | |
1211 | 0 | if (argcnt && !conv->arg_mask) { |
1212 | 0 | memprintf(err_msg, "converter '%s' does not support any args", ckw); |
1213 | 0 | goto out_error; |
1214 | 0 | } |
1215 | | |
1216 | 0 | if (!conv_expr->arg_p) |
1217 | 0 | conv_expr->arg_p = empty_arg_list; |
1218 | |
|
1219 | 0 | if (conv->val_args && !conv->val_args(conv_expr->arg_p, conv, file, line, err_msg)) { |
1220 | 0 | memprintf(err_msg, "invalid args in converter '%s' : %s", ckw, *err_msg); |
1221 | 0 | goto out_error; |
1222 | 0 | } |
1223 | 0 | } |
1224 | | |
1225 | 0 | if (endptr) { |
1226 | | /* end found, let's stop here */ |
1227 | 0 | *endptr = (char *)endt; |
1228 | 0 | } |
1229 | 0 | out: |
1230 | 0 | free(ckw); |
1231 | 0 | return success; |
1232 | | |
1233 | 0 | out_error: |
1234 | 0 | success = 0; |
1235 | 0 | goto out; |
1236 | 0 | } |
1237 | | |
1238 | | /* |
1239 | | * Parse a sample expression configuration: |
1240 | | * fetch keyword followed by format conversion keywords. |
1241 | | * |
1242 | | * <al> is an arg_list serving as a list head to report missing dependencies. |
1243 | | * It may be NULL if such dependencies are not allowed. Otherwise, the caller |
1244 | | * must have set al->ctx if al is set. |
1245 | | * |
1246 | | * Returns a pointer on allocated sample expression structure or NULL in case |
1247 | | * of error, in which case <err_msg> will point to an allocated string that |
1248 | | * brings some info about the failure. It is the caller's responsibility to |
1249 | | * free it. |
1250 | | */ |
1251 | | struct sample_expr *sample_parse_expr(char **str, int *idx, const char *file, int line, char **err_msg, struct arg_list *al, char **endptr) |
1252 | 0 | { |
1253 | 0 | const char *begw; /* beginning of word */ |
1254 | 0 | const char *endw; /* end of word */ |
1255 | 0 | const char *endt; /* end of term */ |
1256 | 0 | struct sample_expr *expr = NULL; |
1257 | 0 | struct sample_fetch *fetch; |
1258 | 0 | char *fkw = NULL; |
1259 | 0 | int err_arg; |
1260 | |
|
1261 | 0 | begw = str[*idx]; |
1262 | 0 | for (endw = begw; is_idchar(*endw); endw++) |
1263 | 0 | ; |
1264 | |
|
1265 | 0 | if (endw == begw) { |
1266 | 0 | memprintf(err_msg, "missing fetch method"); |
1267 | 0 | goto out_error; |
1268 | 0 | } |
1269 | | |
1270 | | /* keep a copy of the current fetch keyword for error reporting */ |
1271 | 0 | fkw = my_strndup(begw, endw - begw); |
1272 | |
|
1273 | 0 | fetch = find_sample_fetch(begw, endw - begw); |
1274 | 0 | if (!fetch) { |
1275 | 0 | memprintf(err_msg, "unknown fetch method '%s'", fkw); |
1276 | 0 | goto out_error; |
1277 | 0 | } |
1278 | | |
1279 | | /* At this point, we have : |
1280 | | * - begw : beginning of the keyword |
1281 | | * - endw : end of the keyword, first character not part of keyword |
1282 | | */ |
1283 | | |
1284 | 0 | if (fetch->out_type >= SMP_TYPES) { |
1285 | 0 | memprintf(err_msg, "returns type of fetch method '%s' is unknown", fkw); |
1286 | 0 | goto out_error; |
1287 | 0 | } |
1288 | | |
1289 | 0 | expr = calloc(1, sizeof(*expr)); |
1290 | 0 | if (!expr) |
1291 | 0 | goto out_error; |
1292 | | |
1293 | 0 | LIST_INIT(&(expr->conv_exprs)); |
1294 | 0 | expr->fetch = fetch; |
1295 | 0 | expr->arg_p = empty_arg_list; |
1296 | | |
1297 | | /* Note that we call the argument parser even with an empty string, |
1298 | | * this allows it to automatically create entries for mandatory |
1299 | | * implicit arguments (eg: local proxy name). |
1300 | | */ |
1301 | 0 | if (al) { |
1302 | 0 | al->kw = expr->fetch->kw; |
1303 | 0 | al->conv = NULL; |
1304 | 0 | } |
1305 | 0 | if (make_arg_list(endw, -1, fetch->arg_mask, &expr->arg_p, err_msg, &endt, &err_arg, al) < 0) { |
1306 | 0 | memprintf(err_msg, "fetch method '%s' : %s", fkw, *err_msg); |
1307 | 0 | goto out_error; |
1308 | 0 | } |
1309 | | |
1310 | | /* now endt is our first char not part of the arg list, typically the |
1311 | | * comma after the sample fetch name or after the closing parenthesis, |
1312 | | * or the NUL char. |
1313 | | */ |
1314 | | |
1315 | 0 | if (!expr->arg_p) { |
1316 | 0 | expr->arg_p = empty_arg_list; |
1317 | 0 | } |
1318 | 0 | else if (fetch->val_args && !fetch->val_args(expr->arg_p, err_msg)) { |
1319 | 0 | memprintf(err_msg, "invalid args in fetch method '%s' : %s", fkw, *err_msg); |
1320 | 0 | goto out_error; |
1321 | 0 | } |
1322 | | |
1323 | 0 | if (!sample_parse_expr_cnv(str, idx, endptr, err_msg, al, file, line, expr, endt)) |
1324 | 0 | goto out_error; |
1325 | | |
1326 | 0 | out: |
1327 | 0 | free(fkw); |
1328 | 0 | return expr; |
1329 | | |
1330 | 0 | out_error: |
1331 | 0 | release_sample_expr(expr); |
1332 | 0 | expr = NULL; |
1333 | 0 | goto out; |
1334 | 0 | } |
1335 | | |
1336 | | /* |
1337 | | * Helper function to process the converter list of a given sample expression |
1338 | | * <expr> using the sample <p> (which is assumed to be properly initialized) |
1339 | | * as input. |
1340 | | * |
1341 | | * Returns 1 on success and 0 on failure. |
1342 | | */ |
1343 | | int sample_process_cnv(struct sample_expr *expr, struct sample *p) |
1344 | 0 | { |
1345 | 0 | struct sample_conv_expr *conv_expr; |
1346 | |
|
1347 | 0 | list_for_each_entry(conv_expr, &expr->conv_exprs, list) { |
1348 | | /* we want to ensure that p->type can be casted into |
1349 | | * conv_expr->conv->in_type. We have 3 possibilities : |
1350 | | * - NULL => not castable. |
1351 | | * - c_none => nothing to do (let's optimize it) |
1352 | | * - other => apply cast and prepare to fail |
1353 | | */ |
1354 | 0 | if (p->data.type != conv_expr->conv->in_type) { |
1355 | 0 | if (!sample_casts[p->data.type][conv_expr->conv->in_type]) |
1356 | 0 | return 0; |
1357 | | |
1358 | 0 | if (sample_casts[p->data.type][conv_expr->conv->in_type] != c_none && |
1359 | 0 | !sample_casts[p->data.type][conv_expr->conv->in_type](p)) |
1360 | 0 | return 0; |
1361 | 0 | } |
1362 | | |
1363 | | /* OK cast succeeded */ |
1364 | 0 | if (!EXEC_CTX_WITH_RET(conv_expr->conv->exec_ctx, |
1365 | 0 | conv_expr->conv->process(conv_expr->arg_p, p, conv_expr->conv->private))) |
1366 | 0 | return 0; |
1367 | 0 | } |
1368 | 0 | return 1; |
1369 | 0 | } |
1370 | | |
1371 | | /* |
1372 | | * Process a fetch + format conversion of defined by the sample expression <expr> |
1373 | | * on request or response considering the <opt> parameter. |
1374 | | * Returns a pointer on a typed sample structure containing the result or NULL if |
1375 | | * sample is not found or when format conversion failed. |
1376 | | * If <p> is not null, function returns results in structure pointed by <p>. |
1377 | | * If <p> is null, functions returns a pointer on a static sample structure. |
1378 | | * |
1379 | | * Note: the fetch functions are required to properly set the return type. The |
1380 | | * conversion functions must do so too. However the cast functions do not need |
1381 | | * to since they're made to cast multiple types according to what is required. |
1382 | | * |
1383 | | * The caller may indicate in <opt> if it considers the result final or not. |
1384 | | * The caller needs to check the SMP_F_MAY_CHANGE flag in p->flags to verify |
1385 | | * if the result is stable or not, according to the following table : |
1386 | | * |
1387 | | * return MAY_CHANGE FINAL Meaning for the sample |
1388 | | * NULL 0 * Not present and will never be (eg: header) |
1389 | | * NULL 1 0 Not present yet, could change (eg: POST param) |
1390 | | * NULL 1 1 Not present yet, will not change anymore |
1391 | | * smp 0 * Present and will not change (eg: header) |
1392 | | * smp 1 0 Present, may change (eg: request length) |
1393 | | * smp 1 1 Present, last known value (eg: request length) |
1394 | | */ |
1395 | | struct sample *sample_process(struct proxy *px, struct session *sess, |
1396 | | struct stream *strm, unsigned int opt, |
1397 | | struct sample_expr *expr, struct sample *p) |
1398 | 0 | { |
1399 | 0 | if (p == NULL) { |
1400 | 0 | p = &temp_smp; |
1401 | 0 | memset(p, 0, sizeof(*p)); |
1402 | 0 | } |
1403 | |
|
1404 | 0 | smp_set_owner(p, px, sess, strm, opt); |
1405 | 0 | if (!EXEC_CTX_WITH_RET(expr->fetch->exec_ctx, |
1406 | 0 | expr->fetch->process(expr->arg_p, p, expr->fetch->kw, expr->fetch->private))) |
1407 | 0 | return NULL; |
1408 | | |
1409 | 0 | if (!sample_process_cnv(expr, p)) |
1410 | 0 | return NULL; |
1411 | 0 | return p; |
1412 | 0 | } |
1413 | | |
1414 | | /* |
1415 | | * Resolve all remaining arguments in proxy <p>. Returns the number of |
1416 | | * errors or 0 if everything is fine. If at least one error is met, it will |
1417 | | * be appended to *err. If *err==NULL it will be allocated first. |
1418 | | */ |
1419 | | int smp_resolve_args(struct proxy *p, char **err) |
1420 | 0 | { |
1421 | 0 | struct arg_list *cur, *bak; |
1422 | 0 | const char *ctx, *where; |
1423 | 0 | const char *conv_ctx, *conv_pre, *conv_pos; |
1424 | 0 | struct userlist *ul; |
1425 | 0 | struct my_regex *reg; |
1426 | 0 | struct arg *arg; |
1427 | 0 | int cfgerr = 0; |
1428 | 0 | int rflags; |
1429 | |
|
1430 | 0 | list_for_each_entry_safe(cur, bak, &p->conf.args.list, list) { |
1431 | 0 | struct proxy *px; |
1432 | 0 | struct server *srv; |
1433 | 0 | struct stktable *t; |
1434 | 0 | char *pname, *sname, *stktname; |
1435 | 0 | char *err2; |
1436 | |
|
1437 | 0 | arg = cur->arg; |
1438 | | |
1439 | | /* prepare output messages */ |
1440 | 0 | conv_pre = conv_pos = conv_ctx = ""; |
1441 | 0 | if (cur->conv) { |
1442 | 0 | conv_ctx = cur->conv; |
1443 | 0 | conv_pre = "conversion keyword '"; |
1444 | 0 | conv_pos = "' for "; |
1445 | 0 | } |
1446 | |
|
1447 | 0 | where = "in"; |
1448 | 0 | ctx = "sample fetch keyword"; |
1449 | 0 | switch (cur->ctx) { |
1450 | 0 | case ARGC_STK: where = "in stick rule in"; break; |
1451 | 0 | case ARGC_TRK: where = "in tracking rule in"; break; |
1452 | 0 | case ARGC_LOG: where = "in log-format string in"; break; |
1453 | 0 | case ARGC_LOGSD: where = "in log-format-sd string in"; break; |
1454 | 0 | case ARGC_HRQ: where = "in http-request expression in"; break; |
1455 | 0 | case ARGC_HRS: where = "in http-response response in"; break; |
1456 | 0 | case ARGC_UIF: where = "in unique-id-format string in"; break; |
1457 | 0 | case ARGC_RDR: where = "in redirect format string in"; break; |
1458 | 0 | case ARGC_CAP: where = "in capture rule in"; break; |
1459 | 0 | case ARGC_ACL: ctx = "ACL keyword"; break; |
1460 | 0 | case ARGC_SRV: where = "in server directive in"; break; |
1461 | 0 | case ARGC_SPOE: where = "in spoe-message directive in"; break; |
1462 | 0 | case ARGC_UBK: where = "in use_backend expression in"; break; |
1463 | 0 | case ARGC_USRV: where = "in use-server or balance expression in"; break; |
1464 | 0 | case ARGC_HERR: where = "in http-error directive in"; break; |
1465 | 0 | case ARGC_OPT: where = "in option directive in"; break; |
1466 | 0 | case ARGC_TCO: where = "in tcp-request connection expression in"; break; |
1467 | 0 | case ARGC_TSE: where = "in tcp-request session expression in"; break; |
1468 | 0 | case ARGC_TRQ: where = "in tcp-request content expression in"; break; |
1469 | 0 | case ARGC_TRS: where = "in tcp-response content expression in"; break; |
1470 | 0 | case ARGC_TCK: where = "in tcp-check expression in"; break; |
1471 | 0 | case ARGC_CFG: where = "in configuration expression in"; break; |
1472 | 0 | case ARGC_CLI: where = "in CLI expression in"; break; |
1473 | 0 | case ARGC_OTEL: where = "in otel-scope directive in"; break; |
1474 | 0 | } |
1475 | | |
1476 | | /* set a few default settings */ |
1477 | 0 | px = p; |
1478 | 0 | pname = p->id; |
1479 | |
|
1480 | 0 | switch (arg->type) { |
1481 | 0 | case ARGT_ID: |
1482 | 0 | err2 = NULL; |
1483 | |
|
1484 | 0 | if (arg->resolve_ptr && !arg->resolve_ptr(arg, &err2)) { |
1485 | 0 | memprintf(err, "%sparsing [%s:%d]: error in identifier '%s' in arg %d of %s%s%s%s '%s' %s proxy '%s' : %s.\n", |
1486 | 0 | *err ? *err : "", cur->file, cur->line, |
1487 | 0 | arg->data.str.area, |
1488 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id, err2); |
1489 | 0 | ha_free(&err2); |
1490 | 0 | cfgerr++; |
1491 | 0 | break; |
1492 | 0 | } |
1493 | 0 | break; |
1494 | | |
1495 | 0 | case ARGT_SRV: |
1496 | 0 | if (!arg->data.str.data) { |
1497 | 0 | memprintf(err, "%sparsing [%s:%d]: missing server name in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1498 | 0 | *err ? *err : "", cur->file, cur->line, |
1499 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1500 | 0 | cfgerr++; |
1501 | 0 | break; |
1502 | 0 | } |
1503 | | |
1504 | | /* we support two formats : "bck/srv" and "srv" */ |
1505 | 0 | sname = strrchr(arg->data.str.area, '/'); |
1506 | |
|
1507 | 0 | if (sname) { |
1508 | 0 | *sname++ = '\0'; |
1509 | 0 | pname = arg->data.str.area; |
1510 | |
|
1511 | 0 | px = proxy_be_by_name(pname); |
1512 | 0 | if (!px) { |
1513 | 0 | memprintf(err, "%sparsing [%s:%d]: unable to find proxy '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1514 | 0 | *err ? *err : "", cur->file, cur->line, pname, |
1515 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1516 | 0 | cfgerr++; |
1517 | 0 | break; |
1518 | 0 | } |
1519 | 0 | } |
1520 | 0 | else { |
1521 | 0 | if (px->cap & PR_CAP_DEF) { |
1522 | 0 | memprintf(err, "%sparsing [%s:%d]: backend name must be set in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1523 | 0 | *err ? *err : "", cur->file, cur->line, |
1524 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1525 | 0 | cfgerr++; |
1526 | 0 | break; |
1527 | 0 | } |
1528 | 0 | sname = arg->data.str.area; |
1529 | 0 | } |
1530 | | |
1531 | 0 | srv = server_find_by_name(px, sname); |
1532 | 0 | if (!srv) { |
1533 | 0 | memprintf(err, "%sparsing [%s:%d]: unable to find server '%s' in proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1534 | 0 | *err ? *err : "", cur->file, cur->line, sname, pname, |
1535 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1536 | 0 | cfgerr++; |
1537 | 0 | break; |
1538 | 0 | } |
1539 | | |
1540 | | /* TODO CLI set-var should not prevent server deletion as var value is instantly resolved. */ |
1541 | 0 | srv->flags |= SRV_F_NAME_REFD; |
1542 | |
|
1543 | 0 | chunk_destroy(&arg->data.str); |
1544 | 0 | arg->unresolved = 0; |
1545 | 0 | arg->data.srv = srv; |
1546 | 0 | break; |
1547 | | |
1548 | 0 | case ARGT_FE: |
1549 | 0 | if (arg->data.str.data) { |
1550 | 0 | pname = arg->data.str.area; |
1551 | 0 | px = proxy_fe_by_name(pname); |
1552 | 0 | } |
1553 | |
|
1554 | 0 | if (!px) { |
1555 | 0 | memprintf(err, "%sparsing [%s:%d]: unable to find frontend '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1556 | 0 | *err ? *err : "", cur->file, cur->line, pname, |
1557 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1558 | 0 | cfgerr++; |
1559 | 0 | break; |
1560 | 0 | } |
1561 | | |
1562 | 0 | if (!(px->cap & PR_CAP_FE)) { |
1563 | 0 | memprintf(err, "%sparsing [%s:%d]: proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s', has not frontend capability.\n", |
1564 | 0 | *err ? *err : "", cur->file, cur->line, pname, |
1565 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1566 | 0 | cfgerr++; |
1567 | 0 | break; |
1568 | 0 | } |
1569 | | |
1570 | | /* TODO CLI set-var should not prevent proxy deletion as var value is instantly resolved. */ |
1571 | 0 | px->flags |= PR_FL_NON_PURGEABLE; |
1572 | |
|
1573 | 0 | chunk_destroy(&arg->data.str); |
1574 | 0 | arg->unresolved = 0; |
1575 | 0 | arg->data.prx = px; |
1576 | 0 | break; |
1577 | | |
1578 | 0 | case ARGT_BE: |
1579 | 0 | if (arg->data.str.data) { |
1580 | 0 | pname = arg->data.str.area; |
1581 | 0 | px = proxy_be_by_name(pname); |
1582 | 0 | } |
1583 | |
|
1584 | 0 | if (!px) { |
1585 | 0 | memprintf(err, "%sparsing [%s:%d]: unable to find backend '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1586 | 0 | *err ? *err : "", cur->file, cur->line, pname, |
1587 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1588 | 0 | cfgerr++; |
1589 | 0 | break; |
1590 | 0 | } |
1591 | | |
1592 | 0 | if (!(px->cap & PR_CAP_BE)) { |
1593 | 0 | memprintf(err, "%sparsing [%s:%d]: proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s', has not backend capability.\n", |
1594 | 0 | *err ? *err : "", cur->file, cur->line, pname, |
1595 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1596 | 0 | cfgerr++; |
1597 | 0 | break; |
1598 | 0 | } |
1599 | | |
1600 | | /* TODO CLI set-var should not prevent proxy deletion as var value is instantly resolved. */ |
1601 | 0 | px->flags |= PR_FL_NON_PURGEABLE; |
1602 | |
|
1603 | 0 | chunk_destroy(&arg->data.str); |
1604 | 0 | arg->unresolved = 0; |
1605 | 0 | arg->data.prx = px; |
1606 | 0 | break; |
1607 | | |
1608 | 0 | case ARGT_TAB: |
1609 | 0 | if (arg->data.str.data) |
1610 | 0 | stktname = arg->data.str.area; |
1611 | 0 | else { |
1612 | 0 | if (px->cap & PR_CAP_DEF) { |
1613 | 0 | memprintf(err, "%sparsing [%s:%d]: table name must be set in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1614 | 0 | *err ? *err : "", cur->file, cur->line, |
1615 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1616 | 0 | cfgerr++; |
1617 | 0 | break; |
1618 | 0 | } |
1619 | 0 | stktname = px->id; |
1620 | 0 | } |
1621 | | |
1622 | 0 | t = stktable_find_by_name(stktname); |
1623 | 0 | if (!t) { |
1624 | 0 | memprintf(err, "%sparsing [%s:%d]: unable to find table '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1625 | 0 | *err ? *err : "", cur->file, cur->line, stktname, |
1626 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1627 | 0 | cfgerr++; |
1628 | 0 | break; |
1629 | 0 | } |
1630 | | |
1631 | 0 | if (!t->size) { |
1632 | 0 | memprintf(err, "%sparsing [%s:%d]: no table in proxy '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1633 | 0 | *err ? *err : "", cur->file, cur->line, stktname, |
1634 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1635 | 0 | cfgerr++; |
1636 | 0 | break; |
1637 | 0 | } |
1638 | | |
1639 | 0 | if (!in_proxies_list(t->proxies_list, p)) { |
1640 | 0 | p->next_stkt_ref = t->proxies_list; |
1641 | 0 | t->proxies_list = p; |
1642 | 0 | } |
1643 | |
|
1644 | 0 | chunk_destroy(&arg->data.str); |
1645 | 0 | arg->unresolved = 0; |
1646 | 0 | arg->data.t = t; |
1647 | 0 | break; |
1648 | | |
1649 | 0 | case ARGT_USR: |
1650 | 0 | if (!arg->data.str.data) { |
1651 | 0 | memprintf(err, "%sparsing [%s:%d]: missing userlist name in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1652 | 0 | *err ? *err : "", cur->file, cur->line, |
1653 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1654 | 0 | cfgerr++; |
1655 | 0 | break; |
1656 | 0 | } |
1657 | | |
1658 | 0 | if (p->uri_auth && p->uri_auth->userlist && |
1659 | 0 | strcmp(p->uri_auth->userlist->name, arg->data.str.area) == 0) |
1660 | 0 | ul = p->uri_auth->userlist; |
1661 | 0 | else |
1662 | 0 | ul = auth_find_userlist(arg->data.str.area); |
1663 | |
|
1664 | 0 | if (!ul) { |
1665 | 0 | memprintf(err, "%sparsing [%s:%d]: unable to find userlist '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1666 | 0 | *err ? *err : "", cur->file, cur->line, |
1667 | 0 | arg->data.str.area, |
1668 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1669 | 0 | cfgerr++; |
1670 | 0 | break; |
1671 | 0 | } |
1672 | | |
1673 | 0 | chunk_destroy(&arg->data.str); |
1674 | 0 | arg->unresolved = 0; |
1675 | 0 | arg->data.usr = ul; |
1676 | 0 | break; |
1677 | | |
1678 | 0 | case ARGT_REG: |
1679 | 0 | if (!arg->data.str.data) { |
1680 | 0 | memprintf(err, "%sparsing [%s:%d]: missing regex in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n", |
1681 | 0 | *err ? *err : "", cur->file, cur->line, |
1682 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id); |
1683 | 0 | cfgerr++; |
1684 | 0 | break; |
1685 | 0 | } |
1686 | | |
1687 | 0 | rflags = 0; |
1688 | 0 | rflags |= (arg->type_flags & ARGF_REG_ICASE) ? REG_ICASE : 0; |
1689 | 0 | err2 = NULL; |
1690 | |
|
1691 | 0 | if (!(reg = regex_comp(arg->data.str.area, !(rflags & REG_ICASE), 1 /* capture substr */, &err2))) { |
1692 | 0 | memprintf(err, "%sparsing [%s:%d]: error in regex '%s' in arg %d of %s%s%s%s '%s' %s proxy '%s' : %s.\n", |
1693 | 0 | *err ? *err : "", cur->file, cur->line, |
1694 | 0 | arg->data.str.area, |
1695 | 0 | cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id, err2); |
1696 | 0 | ha_free(&err2); |
1697 | 0 | cfgerr++; |
1698 | 0 | break; |
1699 | 0 | } |
1700 | | |
1701 | 0 | chunk_destroy(&arg->data.str); |
1702 | 0 | arg->unresolved = 0; |
1703 | 0 | arg->data.reg = reg; |
1704 | 0 | break; |
1705 | | |
1706 | |
|
1707 | 0 | } |
1708 | | |
1709 | 0 | LIST_DELETE(&cur->list); |
1710 | 0 | free(cur); |
1711 | 0 | } /* end of args processing */ |
1712 | | |
1713 | 0 | return cfgerr; |
1714 | 0 | } |
1715 | | |
1716 | | /* |
1717 | | * Process a fetch + format conversion as defined by the sample expression |
1718 | | * <expr> on request or response considering the <opt> parameter. The output is |
1719 | | * not explicitly set to <smp_type>, but shall be compatible with it as |
1720 | | * specified by 'sample_casts' table. If a stable sample can be fetched, or an |
1721 | | * unstable one when <opt> contains SMP_OPT_FINAL, the sample is converted and |
1722 | | * returned without the SMP_F_MAY_CHANGE flag. If an unstable sample is found |
1723 | | * and <opt> does not contain SMP_OPT_FINAL, then the sample is returned as-is |
1724 | | * with its SMP_F_MAY_CHANGE flag so that the caller can check it and decide to |
1725 | | * take actions (eg: wait longer). If a sample could not be found or could not |
1726 | | * be converted, NULL is returned. The caller MUST NOT use the sample if the |
1727 | | * SMP_F_MAY_CHANGE flag is present, as it is used only as a hint that there is |
1728 | | * still hope to get it after waiting longer, and is not converted to string. |
1729 | | * The possible output combinations are the following : |
1730 | | * |
1731 | | * return MAY_CHANGE FINAL Meaning for the sample |
1732 | | * NULL * * Not present and will never be (eg: header) |
1733 | | * smp 0 * Final value converted (eg: header) |
1734 | | * smp 1 0 Not present yet, may appear later (eg: header) |
1735 | | * smp 1 1 never happens (either flag is cleared on output) |
1736 | | */ |
1737 | | struct sample *sample_fetch_as_type(struct proxy *px, struct session *sess, |
1738 | | struct stream *strm, unsigned int opt, |
1739 | | struct sample_expr *expr, int smp_type) |
1740 | 0 | { |
1741 | 0 | struct sample *smp = &temp_smp; |
1742 | |
|
1743 | 0 | memset(smp, 0, sizeof(*smp)); |
1744 | |
|
1745 | 0 | if (!sample_process(px, sess, strm, opt, expr, smp)) { |
1746 | 0 | if ((smp->flags & SMP_F_MAY_CHANGE) && !(opt & SMP_OPT_FINAL)) |
1747 | 0 | return smp; |
1748 | 0 | return NULL; |
1749 | 0 | } |
1750 | | |
1751 | 0 | if (smp->data.type != smp_type) { |
1752 | 0 | if (!sample_casts[smp->data.type][smp_type]) |
1753 | 0 | return NULL; |
1754 | | |
1755 | 0 | if (sample_casts[smp->data.type][smp_type] != c_none && |
1756 | 0 | !sample_casts[smp->data.type][smp_type](smp)) |
1757 | 0 | return NULL; |
1758 | 0 | } |
1759 | | |
1760 | 0 | smp->flags &= ~SMP_F_MAY_CHANGE; |
1761 | 0 | return smp; |
1762 | 0 | } |
1763 | | |
1764 | | static void release_sample_arg(struct arg *p) |
1765 | 0 | { |
1766 | 0 | struct arg *p_back = p; |
1767 | |
|
1768 | 0 | if (!p) |
1769 | 0 | return; |
1770 | | |
1771 | 0 | while (p->type != ARGT_STOP) { |
1772 | 0 | if (p->type == ARGT_STR || p->unresolved) { |
1773 | 0 | chunk_destroy(&p->data.str); |
1774 | 0 | p->unresolved = 0; |
1775 | 0 | } |
1776 | 0 | else if (p->type == ARGT_REG) { |
1777 | 0 | regex_free(p->data.reg); |
1778 | 0 | p->data.reg = NULL; |
1779 | 0 | } |
1780 | 0 | else if (p->type == ARGT_VAR) { |
1781 | 0 | if (p->data.var.flags & VDF_NAME_ALLOCATED) |
1782 | 0 | ha_free((char **)&p->data.var.name); |
1783 | 0 | } |
1784 | 0 | p++; |
1785 | 0 | } |
1786 | |
|
1787 | 0 | if (p_back != empty_arg_list) |
1788 | 0 | free(p_back); |
1789 | 0 | } |
1790 | | |
1791 | | void release_sample_expr(struct sample_expr *expr) |
1792 | 0 | { |
1793 | 0 | struct sample_conv_expr *conv_expr, *conv_exprb; |
1794 | |
|
1795 | 0 | if (!expr) |
1796 | 0 | return; |
1797 | | |
1798 | 0 | list_for_each_entry_safe(conv_expr, conv_exprb, &expr->conv_exprs, list) { |
1799 | 0 | LIST_DELETE(&conv_expr->list); |
1800 | 0 | release_sample_arg(conv_expr->arg_p); |
1801 | 0 | free(conv_expr); |
1802 | 0 | } |
1803 | |
|
1804 | 0 | release_sample_arg(expr->arg_p); |
1805 | 0 | free(expr); |
1806 | 0 | } |
1807 | | |
1808 | | /*****************************************************************/ |
1809 | | /* Sample format convert functions */ |
1810 | | /* These functions set the data type on return. */ |
1811 | | /*****************************************************************/ |
1812 | | |
1813 | | static int sample_conv_debug(const struct arg *arg_p, struct sample *smp, void *private) |
1814 | 0 | { |
1815 | 0 | int i; |
1816 | 0 | struct sample tmp; |
1817 | 0 | struct buffer *buf; |
1818 | 0 | struct sink *sink; |
1819 | 0 | struct ist line; |
1820 | 0 | char *pfx; |
1821 | |
|
1822 | 0 | buf = alloc_trash_chunk(); |
1823 | 0 | if (!buf) |
1824 | 0 | goto end; |
1825 | | |
1826 | 0 | sink = (struct sink *)arg_p[1].data.ptr; |
1827 | 0 | BUG_ON(!sink); |
1828 | |
|
1829 | 0 | pfx = arg_p[0].data.str.area; |
1830 | 0 | BUG_ON(!pfx); |
1831 | |
|
1832 | 0 | chunk_printf(buf, "[debug] %s: type=%s ", pfx, smp_to_type[smp->data.type]); |
1833 | 0 | if (!sample_casts[smp->data.type][SMP_T_STR]) |
1834 | 0 | goto nocast; |
1835 | | |
1836 | | /* Copy sample fetch. This puts the sample as const, the |
1837 | | * cast will copy data if a transformation is required. |
1838 | | */ |
1839 | 0 | memcpy(&tmp, smp, sizeof(struct sample)); |
1840 | 0 | tmp.flags = SMP_F_CONST; |
1841 | |
|
1842 | 0 | if (!sample_casts[smp->data.type][SMP_T_STR](&tmp)) |
1843 | 0 | goto nocast; |
1844 | | |
1845 | | /* Display the displayable chars*. */ |
1846 | 0 | b_putchr(buf, '<'); |
1847 | 0 | for (i = 0; i < tmp.data.u.str.data; i++) { |
1848 | 0 | if (isprint((unsigned char)tmp.data.u.str.area[i])) |
1849 | 0 | b_putchr(buf, tmp.data.u.str.area[i]); |
1850 | 0 | else |
1851 | 0 | b_putchr(buf, '.'); |
1852 | 0 | } |
1853 | 0 | b_putchr(buf, '>'); |
1854 | |
|
1855 | 0 | done: |
1856 | 0 | line = ist2(buf->area, buf->data); |
1857 | 0 | sink_write(sink, LOG_HEADER_NONE, 0, &line, 1); |
1858 | 0 | end: |
1859 | 0 | free_trash_chunk(buf); |
1860 | 0 | return 1; |
1861 | 0 | nocast: |
1862 | 0 | chunk_appendf(buf, "(undisplayable)"); |
1863 | 0 | goto done; |
1864 | 0 | } |
1865 | | |
1866 | | // This function checks the "debug" converter's arguments. |
1867 | | static int smp_check_debug(struct arg *args, struct sample_conv *conv, |
1868 | | const char *file, int line, char **err) |
1869 | 0 | { |
1870 | 0 | const char *name = "buf0"; |
1871 | 0 | struct sink *sink = NULL; |
1872 | |
|
1873 | 0 | if (args[0].type != ARGT_STR) { |
1874 | | /* optional prefix */ |
1875 | 0 | args[0].data.str.area = ""; |
1876 | 0 | args[0].data.str.data = 0; |
1877 | 0 | } |
1878 | |
|
1879 | 0 | if (args[1].type == ARGT_STR) |
1880 | 0 | name = args[1].data.str.area; |
1881 | |
|
1882 | 0 | sink = sink_find_early(name, "debug converter", file, line); |
1883 | 0 | if (!sink) { |
1884 | 0 | memprintf(err, "Memory error while setting up sink '%s'", name); |
1885 | 0 | return 0; |
1886 | 0 | } |
1887 | | |
1888 | 0 | chunk_destroy(&args[1].data.str); |
1889 | 0 | args[1].type = ARGT_PTR; |
1890 | 0 | args[1].data.ptr = sink; |
1891 | 0 | return 1; |
1892 | 0 | } |
1893 | | |
1894 | | static int sample_conv_base642bin(const struct arg *arg_p, struct sample *smp, void *private) |
1895 | 0 | { |
1896 | 0 | struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); |
1897 | 0 | int bin_len; |
1898 | |
|
1899 | 0 | if (!trash) |
1900 | 0 | return 0; |
1901 | 0 | trash->data = 0; |
1902 | 0 | bin_len = base64dec(smp->data.u.str.area, smp->data.u.str.data, |
1903 | 0 | trash->area, trash->size); |
1904 | 0 | if (bin_len < 0) |
1905 | 0 | return 0; |
1906 | | |
1907 | 0 | trash->data = bin_len; |
1908 | 0 | smp->data.u.str = *trash; |
1909 | 0 | smp->data.type = SMP_T_BIN; |
1910 | 0 | smp->flags &= ~SMP_F_CONST; |
1911 | 0 | return 1; |
1912 | 0 | } |
1913 | | |
1914 | | static int sample_conv_base64url2bin(const struct arg *arg_p, struct sample *smp, void *private) |
1915 | 0 | { |
1916 | 0 | struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); |
1917 | 0 | int bin_len; |
1918 | |
|
1919 | 0 | if (!trash) |
1920 | 0 | return 0; |
1921 | 0 | trash->data = 0; |
1922 | 0 | bin_len = base64urldec(smp->data.u.str.area, smp->data.u.str.data, |
1923 | 0 | trash->area, trash->size); |
1924 | 0 | if (bin_len < 0) |
1925 | 0 | return 0; |
1926 | | |
1927 | 0 | trash->data = bin_len; |
1928 | 0 | smp->data.u.str = *trash; |
1929 | 0 | smp->data.type = SMP_T_BIN; |
1930 | 0 | smp->flags &= ~SMP_F_CONST; |
1931 | 0 | return 1; |
1932 | 0 | } |
1933 | | |
1934 | | static int sample_conv_bin2base64(const struct arg *arg_p, struct sample *smp, void *private) |
1935 | 0 | { |
1936 | 0 | struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, (smp->data.u.str.data+2) * 4 / 3); |
1937 | 0 | int b64_len; |
1938 | |
|
1939 | 0 | if (!trash) |
1940 | 0 | return 0; |
1941 | 0 | trash->data = 0; |
1942 | 0 | b64_len = a2base64(smp->data.u.str.area, smp->data.u.str.data, |
1943 | 0 | trash->area, trash->size); |
1944 | 0 | if (b64_len < 0) |
1945 | 0 | return 0; |
1946 | | |
1947 | 0 | trash->data = b64_len; |
1948 | 0 | smp->data.u.str = *trash; |
1949 | 0 | smp->data.type = SMP_T_STR; |
1950 | 0 | smp->flags &= ~SMP_F_CONST; |
1951 | 0 | return 1; |
1952 | 0 | } |
1953 | | |
1954 | | static int sample_conv_bin2base64url(const struct arg *arg_p, struct sample *smp, void *private) |
1955 | 0 | { |
1956 | 0 | struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, (smp->data.u.str.data+2) * 4 / 3); |
1957 | 0 | int b64_len; |
1958 | |
|
1959 | 0 | if (!trash) |
1960 | 0 | return 0; |
1961 | 0 | trash->data = 0; |
1962 | 0 | b64_len = a2base64url(smp->data.u.str.area, smp->data.u.str.data, |
1963 | 0 | trash->area, trash->size); |
1964 | 0 | if (b64_len < 0) |
1965 | 0 | return 0; |
1966 | | |
1967 | 0 | trash->data = b64_len; |
1968 | 0 | smp->data.u.str = *trash; |
1969 | 0 | smp->data.type = SMP_T_STR; |
1970 | 0 | smp->flags &= ~SMP_F_CONST; |
1971 | 0 | return 1; |
1972 | 0 | } |
1973 | | |
1974 | | /* This function returns a sample struct filled with the conversion of variable |
1975 | | * <var> to sample type <type> (SMP_T_*), via a cast to the target type. If the |
1976 | | * variable cannot be retrieved or casted, 0 is returned, otherwise 1. |
1977 | | * |
1978 | | * Keep in mind that the sample content may be written to a pre-allocated |
1979 | | * trash chunk as returned by get_trash_chunk(). |
1980 | | */ |
1981 | | int sample_conv_var2smp(const struct var_desc *var, struct sample *smp, int type) |
1982 | 0 | { |
1983 | 0 | if (!vars_get_by_desc(var, smp, NULL)) |
1984 | 0 | return 0; |
1985 | 0 | if (!sample_casts[smp->data.type][type]) |
1986 | 0 | return 0; |
1987 | 0 | if (!sample_casts[smp->data.type][type](smp)) |
1988 | 0 | return 0; |
1989 | 0 | return 1; |
1990 | 0 | } |
1991 | | |
1992 | | static int sample_conv_sha1(const struct arg *arg_p, struct sample *smp, void *private) |
1993 | 0 | { |
1994 | 0 | blk_SHA_CTX ctx; |
1995 | 0 | struct buffer *trash = get_trash_chunk(); |
1996 | |
|
1997 | 0 | memset(&ctx, 0, sizeof(ctx)); |
1998 | |
|
1999 | 0 | blk_SHA1_Init(&ctx); |
2000 | 0 | blk_SHA1_Update(&ctx, smp->data.u.str.area, smp->data.u.str.data); |
2001 | 0 | blk_SHA1_Final((unsigned char *) trash->area, &ctx); |
2002 | |
|
2003 | 0 | trash->data = 20; |
2004 | 0 | smp->data.u.str = *trash; |
2005 | 0 | smp->data.type = SMP_T_BIN; |
2006 | 0 | smp->flags &= ~SMP_F_CONST; |
2007 | 0 | return 1; |
2008 | 0 | } |
2009 | | |
2010 | | /* This function returns a sample struct filled with an <arg> content. |
2011 | | * If the <arg> contains a string, it is returned in the sample flagged as |
2012 | | * SMP_F_CONST. If the <arg> contains a variable descriptor, the sample is |
2013 | | * filled with the content of the variable by using vars_get_by_desc(). |
2014 | | * |
2015 | | * Keep in mind that the sample content may be written to a pre-allocated |
2016 | | * trash chunk as returned by get_trash_chunk(). |
2017 | | * |
2018 | | * This function returns 0 if an error occurs, otherwise it returns 1. |
2019 | | */ |
2020 | | int sample_conv_var2smp_str(const struct arg *arg, struct sample *smp) |
2021 | 0 | { |
2022 | 0 | switch (arg->type) { |
2023 | 0 | case ARGT_STR: |
2024 | 0 | smp->data.type = SMP_T_STR; |
2025 | 0 | smp->data.u.str = arg->data.str; |
2026 | 0 | smp->flags = SMP_F_CONST; |
2027 | 0 | return 1; |
2028 | 0 | case ARGT_VAR: |
2029 | 0 | return sample_conv_var2smp(&arg->data.var, smp, SMP_T_STR); |
2030 | 0 | default: |
2031 | 0 | return 0; |
2032 | 0 | } |
2033 | 0 | } |
2034 | | |
2035 | | static int sample_conv_2dec_check(struct arg *args, struct sample_conv *conv, |
2036 | | const char *file, int line, char **err) |
2037 | 0 | { |
2038 | 0 | if (args[1].data.sint <= 0 || args[1].data.sint > sizeof(unsigned long long)) { |
2039 | 0 | memprintf(err, "chunk_size out of [1..%u] range (%lld)", (uint)sizeof(unsigned long long), args[1].data.sint); |
2040 | 0 | return 0; |
2041 | 0 | } |
2042 | | |
2043 | 0 | if (args[2].data.sint != 0 && args[2].data.sint != 1) { |
2044 | 0 | memprintf(err, "Unsupported truncate value (%lld)", args[2].data.sint); |
2045 | 0 | return 0; |
2046 | 0 | } |
2047 | | |
2048 | 0 | return 1; |
2049 | 0 | } |
2050 | | |
2051 | | /* Converts big-endian/little-endian binary input sample to a string containing an unsigned |
2052 | | * integer number per <chunk_size> input bytes separated with <separator>. |
2053 | | * Optional <truncate> flag indicates if input is truncated at <chunk_size> |
2054 | | * boundaries. |
2055 | | * Arguments: separator (string), chunk_size (integer), truncate (0,1), big endian (0,1) |
2056 | | */ |
2057 | | static int sample_conv_2dec(const struct arg *args, struct sample *smp, void *private, int be) |
2058 | 0 | { |
2059 | 0 | struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); |
2060 | 0 | const int last = args[2].data.sint ? smp->data.u.str.data - args[1].data.sint + 1 : smp->data.u.str.data; |
2061 | 0 | int max_size; |
2062 | 0 | int i; |
2063 | 0 | int start; |
2064 | 0 | int ptr = 0; |
2065 | 0 | unsigned long long number; |
2066 | 0 | char *pos; |
2067 | |
|
2068 | 0 | if (!trash) |
2069 | 0 | return 0; |
2070 | 0 | max_size = trash->size - 2; |
2071 | 0 | trash->data = 0; |
2072 | |
|
2073 | 0 | while (ptr < last && trash->data <= max_size) { |
2074 | 0 | start = trash->data; |
2075 | 0 | if (ptr) { |
2076 | | /* Add separator */ |
2077 | 0 | memcpy(trash->area + trash->data, args[0].data.str.area, args[0].data.str.data); |
2078 | 0 | trash->data += args[0].data.str.data; |
2079 | 0 | } |
2080 | 0 | else |
2081 | 0 | max_size -= args[0].data.str.data; |
2082 | | |
2083 | | /* Add integer */ |
2084 | 0 | for (number = 0, i = 0; i < args[1].data.sint && ptr < smp->data.u.str.data; i++) { |
2085 | 0 | if (be) |
2086 | 0 | number = (number << 8) + (unsigned char)smp->data.u.str.area[ptr++]; |
2087 | 0 | else |
2088 | 0 | number |= (unsigned char)smp->data.u.str.area[ptr++] << (i*8); |
2089 | 0 | } |
2090 | |
|
2091 | 0 | pos = ulltoa(number, trash->area + trash->data, trash->size - trash->data); |
2092 | 0 | if (pos) |
2093 | 0 | trash->data = pos - trash->area; |
2094 | 0 | else { |
2095 | 0 | trash->data = start; |
2096 | 0 | break; |
2097 | 0 | } |
2098 | 0 | } |
2099 | |
|
2100 | 0 | smp->data.u.str = *trash; |
2101 | 0 | smp->data.type = SMP_T_STR; |
2102 | 0 | smp->flags &= ~SMP_F_CONST; |
2103 | 0 | return 1; |
2104 | 0 | } |
2105 | | |
2106 | | /* Converts big-endian binary input sample to a string containing an unsigned |
2107 | | * integer number per <chunk_size> input bytes separated with <separator>. |
2108 | | * Optional <truncate> flag indicates if input is truncated at <chunk_size> |
2109 | | * boundaries. |
2110 | | * Arguments: separator (string), chunk_size (integer), truncate (0,1) |
2111 | | */ |
2112 | | static int sample_conv_be2dec(const struct arg *args, struct sample *smp, void *private) |
2113 | 0 | { |
2114 | 0 | return sample_conv_2dec(args, smp, private, 1); |
2115 | 0 | } |
2116 | | |
2117 | | /* Converts little-endian binary input sample to a string containing an unsigned |
2118 | | * integer number per <chunk_size> input bytes separated with <separator>. |
2119 | | * Optional <truncate> flag indicates if input is truncated at <chunk_size> |
2120 | | * boundaries. |
2121 | | * Arguments: separator (string), chunk_size (integer), truncate (0,1) |
2122 | | */ |
2123 | | static int sample_conv_le2dec(const struct arg *args, struct sample *smp, void *private) |
2124 | 0 | { |
2125 | 0 | return sample_conv_2dec(args, smp, private, 0); |
2126 | 0 | } |
2127 | | |
2128 | | static int sample_conv_be2hex_check(struct arg *args, struct sample_conv *conv, |
2129 | | const char *file, int line, char **err) |
2130 | 0 | { |
2131 | 0 | if (args[1].data.sint <= 0 && (args[0].data.str.data > 0 || args[2].data.sint != 0)) { |
2132 | 0 | memprintf(err, "chunk_size needs to be positive (%lld)", args[1].data.sint); |
2133 | 0 | return 0; |
2134 | 0 | } |
2135 | | |
2136 | 0 | if (args[2].data.sint != 0 && args[2].data.sint != 1) { |
2137 | 0 | memprintf(err, "Unsupported truncate value (%lld)", args[2].data.sint); |
2138 | 0 | return 0; |
2139 | 0 | } |
2140 | | |
2141 | 0 | return 1; |
2142 | 0 | } |
2143 | | |
2144 | | /* Converts big-endian binary input sample to a hex string containing two hex |
2145 | | * digits per input byte. <separator> is put every <chunk_size> binary input |
2146 | | * bytes if specified. Optional <truncate> flag indicates if input is truncated |
2147 | | * at <chunk_size> boundaries. |
2148 | | * Arguments: separator (string), chunk_size (integer), truncate (0,1) |
2149 | | */ |
2150 | | static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *private) |
2151 | 0 | { |
2152 | 0 | struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.size); |
2153 | 0 | int chunk_size = args[1].data.sint; |
2154 | 0 | const int last = args[2].data.sint ? smp->data.u.str.data - chunk_size + 1 : smp->data.u.str.data; |
2155 | 0 | int i; |
2156 | 0 | size_t max_size; |
2157 | 0 | int ptr = 0; |
2158 | 0 | unsigned char c; |
2159 | |
|
2160 | 0 | if (!trash) |
2161 | 0 | return 0; |
2162 | 0 | trash->data = 0; |
2163 | 0 | if (args[0].data.str.data == 0 && args[2].data.sint == 0) |
2164 | 0 | chunk_size = smp->data.u.str.data; |
2165 | 0 | if (2 * (size_t)chunk_size + args[0].data.str.data > trash->size) |
2166 | 0 | return 0; |
2167 | 0 | max_size = trash->size - 2 * (size_t)chunk_size; |
2168 | |
|
2169 | 0 | while (ptr < last && trash->data <= max_size) { |
2170 | 0 | if (ptr) { |
2171 | | /* Add separator */ |
2172 | 0 | memcpy(trash->area + trash->data, args[0].data.str.area, args[0].data.str.data); |
2173 | 0 | trash->data += args[0].data.str.data; |
2174 | 0 | } |
2175 | 0 | else |
2176 | 0 | max_size -= args[0].data.str.data; |
2177 | | |
2178 | | /* Add hex */ |
2179 | 0 | for (i = 0; i < chunk_size && ptr < smp->data.u.str.data; i++) { |
2180 | 0 | c = smp->data.u.str.area[ptr++]; |
2181 | 0 | trash->area[trash->data++] = hextab[(c >> 4) & 0xF]; |
2182 | 0 | trash->area[trash->data++] = hextab[c & 0xF]; |
2183 | 0 | } |
2184 | 0 | } |
2185 | |
|
2186 | 0 | smp->data.u.str = *trash; |
2187 | 0 | smp->data.type = SMP_T_STR; |
2188 | 0 | smp->flags &= ~SMP_F_CONST; |
2189 | 0 | return 1; |
2190 | 0 | } |
2191 | | |
2192 | | static int sample_conv_bin2hex(const struct arg *arg_p, struct sample *smp, void *private) |
2193 | 0 | { |
2194 | 0 | struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.data*2); |
2195 | 0 | unsigned char c; |
2196 | 0 | int ptr = 0; |
2197 | |
|
2198 | 0 | if (!trash) |
2199 | 0 | return 0; |
2200 | 0 | trash->data = 0; |
2201 | 0 | while (ptr < smp->data.u.str.data && trash->data <= trash->size - 2) { |
2202 | 0 | c = smp->data.u.str.area[ptr++]; |
2203 | 0 | trash->area[trash->data++] = hextab[(c >> 4) & 0xF]; |
2204 | 0 | trash->area[trash->data++] = hextab[c & 0xF]; |
2205 | 0 | } |
2206 | 0 | smp->data.u.str = *trash; |
2207 | 0 | smp->data.type = SMP_T_STR; |
2208 | 0 | smp->flags &= ~SMP_F_CONST; |
2209 | 0 | return 1; |
2210 | 0 | } |
2211 | | |
2212 | | static int sample_conv_bin2base2(const struct arg *arg_p, struct sample *smp, void *private) |
2213 | 0 | { |
2214 | 0 | struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.data*8); |
2215 | 0 | unsigned char c; |
2216 | 0 | int ptr = 0; |
2217 | 0 | int bit = 0; |
2218 | |
|
2219 | 0 | if (!trash) |
2220 | 0 | return 0; |
2221 | 0 | trash->data = 0; |
2222 | 0 | while (ptr < smp->data.u.str.data && trash->data <= trash->size - 8) { |
2223 | 0 | c = smp->data.u.str.area[ptr++]; |
2224 | 0 | for (bit = 7; bit >= 0; bit--) |
2225 | 0 | trash->area[trash->data++] = c & (1 << bit) ? '1' : '0'; |
2226 | 0 | } |
2227 | 0 | smp->data.u.str = *trash; |
2228 | 0 | smp->data.type = SMP_T_STR; |
2229 | 0 | smp->flags &= ~SMP_F_CONST; |
2230 | 0 | return 1; |
2231 | 0 | } |
2232 | | |
2233 | | static int sample_conv_hex2int(const struct arg *arg_p, struct sample *smp, void *private) |
2234 | 0 | { |
2235 | 0 | long long int n = 0; |
2236 | 0 | int i, c; |
2237 | |
|
2238 | 0 | for (i = 0; i < smp->data.u.str.data; i++) { |
2239 | 0 | if ((c = hex2i(smp->data.u.str.area[i])) < 0) |
2240 | 0 | return 0; |
2241 | 0 | n = (n << 4) + c; |
2242 | 0 | } |
2243 | | |
2244 | 0 | smp->data.u.sint = n; |
2245 | 0 | smp->data.type = SMP_T_SINT; |
2246 | 0 | smp->flags &= ~SMP_F_CONST; |
2247 | 0 | return 1; |
2248 | 0 | } |
2249 | | |
2250 | | /* hashes the binary input into a 32-bit unsigned int */ |
2251 | | static int sample_conv_djb2(const struct arg *arg_p, struct sample *smp, void *private) |
2252 | 0 | { |
2253 | 0 | smp->data.u.sint = hash_djb2(smp->data.u.str.area, |
2254 | 0 | smp->data.u.str.data); |
2255 | 0 | if (arg_p->data.sint) |
2256 | 0 | smp->data.u.sint = full_hash(smp->data.u.sint); |
2257 | 0 | smp->data.type = SMP_T_SINT; |
2258 | 0 | return 1; |
2259 | 0 | } |
2260 | | |
2261 | | static int sample_conv_length(const struct arg *arg_p, struct sample *smp, void *private) |
2262 | 0 | { |
2263 | 0 | int i = smp->data.u.str.data; |
2264 | 0 | smp->data.u.sint = i; |
2265 | 0 | smp->data.type = SMP_T_SINT; |
2266 | 0 | return 1; |
2267 | 0 | } |
2268 | | |
2269 | | |
2270 | | static int sample_conv_str2lower(const struct arg *arg_p, struct sample *smp, void *private) |
2271 | 0 | { |
2272 | 0 | int i; |
2273 | |
|
2274 | 0 | if (!smp_make_rw(smp)) |
2275 | 0 | return 0; |
2276 | | |
2277 | 0 | for (i = 0; i < smp->data.u.str.data; i++) { |
2278 | 0 | if ((smp->data.u.str.area[i] >= 'A') && (smp->data.u.str.area[i] <= 'Z')) |
2279 | 0 | smp->data.u.str.area[i] += 'a' - 'A'; |
2280 | 0 | } |
2281 | 0 | return 1; |
2282 | 0 | } |
2283 | | |
2284 | | static int sample_conv_str2upper(const struct arg *arg_p, struct sample *smp, void *private) |
2285 | 0 | { |
2286 | 0 | int i; |
2287 | |
|
2288 | 0 | if (!smp_make_rw(smp)) |
2289 | 0 | return 0; |
2290 | | |
2291 | 0 | for (i = 0; i < smp->data.u.str.data; i++) { |
2292 | 0 | if ((smp->data.u.str.area[i] >= 'a') && (smp->data.u.str.area[i] <= 'z')) |
2293 | 0 | smp->data.u.str.area[i] += 'A' - 'a'; |
2294 | 0 | } |
2295 | 0 | return 1; |
2296 | 0 | } |
2297 | | |
2298 | | /* Reverses the input string byte by byte. */ |
2299 | | static int sample_conv_reverse(const struct arg *arg_p, struct sample *smp, void *private) |
2300 | 0 | { |
2301 | 0 | const char *input = smp->data.u.str.area; |
2302 | 0 | struct buffer *trash; |
2303 | 0 | int input_len = smp->data.u.str.data; |
2304 | 0 | int i; |
2305 | |
|
2306 | 0 | trash = get_best_trash_chunk(&smp->data.u.str, input_len+1); |
2307 | 0 | if (!trash) |
2308 | 0 | return 0; |
2309 | | |
2310 | 0 | for (i = 0; i < input_len; i++) |
2311 | 0 | trash->area[i] = input[input_len - 1 - i]; |
2312 | |
|
2313 | 0 | trash->area[input_len] = 0; |
2314 | 0 | trash->data = input_len; |
2315 | 0 | smp->data.u.str = *trash; |
2316 | 0 | smp->data.type = SMP_T_STR; |
2317 | 0 | smp->flags &= ~SMP_F_CONST; |
2318 | 0 | return 1; |
2319 | 0 | } |
2320 | | |
2321 | | /* Reverses the order of labels in an FQDN-like string. A single trailing dot |
2322 | | * on input is ignored. Empty labels are rejected. |
2323 | | */ |
2324 | | static int sample_conv_reverse_dom(const struct arg *arg_p, struct sample *smp, void *private) |
2325 | 0 | { |
2326 | 0 | const char *input = smp->data.u.str.area; |
2327 | 0 | struct buffer *trash; |
2328 | 0 | int input_len = smp->data.u.str.data; |
2329 | 0 | int out = 0; |
2330 | 0 | int label_end; |
2331 | 0 | int label_start; |
2332 | 0 | int label_len; |
2333 | |
|
2334 | 0 | if (!input_len) |
2335 | 0 | return 0; |
2336 | | |
2337 | 0 | if (input[input_len - 1] == '.') { |
2338 | 0 | input_len--; |
2339 | 0 | if (!input_len) |
2340 | 0 | return 0; |
2341 | 0 | } |
2342 | | |
2343 | 0 | if (input[0] == '.') |
2344 | 0 | return 0; |
2345 | | |
2346 | 0 | trash = get_best_trash_chunk(&smp->data.u.str, input_len+1); |
2347 | 0 | if (!trash) |
2348 | 0 | return 0; |
2349 | | |
2350 | 0 | label_end = input_len; |
2351 | 0 | while (label_end > 0) { |
2352 | 0 | label_start = label_end - 1; |
2353 | 0 | while (label_start >= 0 && input[label_start] != '.') |
2354 | 0 | label_start--; |
2355 | 0 | label_start++; |
2356 | |
|
2357 | 0 | if (label_start == label_end) |
2358 | 0 | return 0; |
2359 | | |
2360 | 0 | label_len = label_end - label_start; |
2361 | 0 | memcpy(trash->area + out, input + label_start, label_len); |
2362 | 0 | out += label_len; |
2363 | |
|
2364 | 0 | if (label_start == 0) |
2365 | 0 | break; |
2366 | | |
2367 | 0 | trash->area[out++] = '.'; |
2368 | 0 | label_end = label_start - 1; |
2369 | 0 | } |
2370 | | |
2371 | 0 | trash->area[out] = 0; |
2372 | 0 | trash->data = out; |
2373 | 0 | smp->data.u.str = *trash; |
2374 | 0 | smp->data.type = SMP_T_STR; |
2375 | 0 | smp->flags &= ~SMP_F_CONST; |
2376 | 0 | return 1; |
2377 | 0 | } |
2378 | | |
2379 | | /* takes the IPv4 mask in args[0] and an optional IPv6 mask in args[1] */ |
2380 | | static int sample_conv_ipmask(const struct arg *args, struct sample *smp, void *private) |
2381 | 0 | { |
2382 | | /* Attempt to convert to IPv4 to apply the correct mask. */ |
2383 | 0 | c_ipv62ip(smp); |
2384 | |
|
2385 | 0 | if (smp->data.type == SMP_T_IPV4) { |
2386 | 0 | smp->data.u.ipv4.s_addr &= args[0].data.ipv4.s_addr; |
2387 | 0 | smp->data.type = SMP_T_IPV4; |
2388 | 0 | } |
2389 | 0 | else if (smp->data.type == SMP_T_IPV6) { |
2390 | | /* IPv6 cannot be converted without an IPv6 mask. */ |
2391 | 0 | if (args[1].type != ARGT_IPV6) |
2392 | 0 | return 0; |
2393 | | |
2394 | 0 | write_u64(&smp->data.u.ipv6.s6_addr[0], |
2395 | 0 | read_u64(&smp->data.u.ipv6.s6_addr[0]) & read_u64(&args[1].data.ipv6.s6_addr[0])); |
2396 | 0 | write_u64(&smp->data.u.ipv6.s6_addr[8], |
2397 | 0 | read_u64(&smp->data.u.ipv6.s6_addr[8]) & read_u64(&args[1].data.ipv6.s6_addr[8])); |
2398 | 0 | smp->data.type = SMP_T_IPV6; |
2399 | 0 | } |
2400 | | |
2401 | 0 | return 1; |
2402 | 0 | } |
2403 | | |
2404 | | /* |
2405 | | * This function implement a conversion specifier seeker for %N so it could be |
2406 | | * replaced before doing strftime. |
2407 | | * |
2408 | | * <format> is the input format string which is used as a haystack |
2409 | | * |
2410 | | * The function fills multiple variables: |
2411 | | * <skip> is the len of the conversion specifier string which was found (ex: strlen(%N):2, strlen(%3N):3 strlen(%123N): 5) |
2412 | | * <width> is the width argument, default width is 9 (ex: %3N: 3, %4N: 4: %N: 9, %5N: 5) |
2413 | | * |
2414 | | * Returns a ptr to the first character of the conversion specifier or NULL if not found |
2415 | | */ |
2416 | | static const char *lookup_convspec_N(const char *format, int *skip, int *width) |
2417 | 0 | { |
2418 | 0 | const char *p, *needle; |
2419 | 0 | const char *digits; |
2420 | 0 | int state; |
2421 | |
|
2422 | 0 | p = format; |
2423 | | |
2424 | | /* this looks for % in loop. The iteration stops when a %N conversion |
2425 | | * specifier was found or there is no '%' anymore */ |
2426 | 0 | lookagain: |
2427 | 0 | while (p && *p) { |
2428 | 0 | state = 0; |
2429 | 0 | digits = NULL; |
2430 | |
|
2431 | 0 | p = needle = strchr(p, '%'); |
2432 | | /* Once we find a % we try to move forward in the string |
2433 | | * |
2434 | | * state 0: found % |
2435 | | * state 1: digits (precision) |
2436 | | * state 2: N |
2437 | | */ |
2438 | 0 | while (p && *p) { |
2439 | 0 | switch (state) { |
2440 | 0 | case 0: |
2441 | 0 | state = 1; |
2442 | 0 | break; |
2443 | | |
2444 | 0 | case 1: |
2445 | 0 | if (isdigit((unsigned char)*p) && !digits) /* set the start of the digits */ |
2446 | 0 | digits = p; |
2447 | |
|
2448 | 0 | if (isdigit((unsigned char)*p)) |
2449 | 0 | break; |
2450 | 0 | else |
2451 | 0 | state = 2; |
2452 | | /* if this is not a number anymore, we |
2453 | | * don't want to increment p but try the |
2454 | | * next state directly */ |
2455 | 0 | __fallthrough; |
2456 | 0 | case 2: |
2457 | 0 | if (*p == 'N') |
2458 | 0 | goto found; |
2459 | 0 | else |
2460 | | /* this was not a %N, start again */ |
2461 | 0 | goto lookagain; |
2462 | 0 | break; |
2463 | 0 | } |
2464 | 0 | p++; |
2465 | 0 | } |
2466 | 0 | } |
2467 | | |
2468 | 0 | *skip = 0; |
2469 | 0 | *width = 0; |
2470 | 0 | return NULL; |
2471 | | |
2472 | 0 | found: |
2473 | 0 | *skip = p - needle + 1; |
2474 | 0 | if (digits) |
2475 | 0 | *width = atoi(digits); |
2476 | 0 | else |
2477 | 0 | *width = 9; |
2478 | 0 | return needle; |
2479 | 0 | } |
2480 | | |
2481 | | /* |
2482 | | * strftime(3) does not implement nanoseconds, but we still want them in our |
2483 | | * date format. |
2484 | | * |
2485 | | * This function implements %N like in date(1) which gives you the nanoseconds part of the timestamp |
2486 | | * An optional field width can be specified, a maximum width of 9 is supported (ex: %3N %6N %9N) |
2487 | | * |
2488 | | * <format> is the format string |
2489 | | * <curr_date> in seconds since epoch |
2490 | | * <ns> only the nanoseconds part of the timestamp |
2491 | | * <local> chose the localtime instead of UTC time |
2492 | | * |
2493 | | * Return the results of strftime in the trash buffer |
2494 | | */ |
2495 | | static struct buffer *conv_time_common(const char *format, time_t curr_date, uint64_t ns, int local) |
2496 | 0 | { |
2497 | 0 | struct buffer *tmp_format = NULL; |
2498 | 0 | struct buffer *res = NULL; |
2499 | 0 | struct tm tm; |
2500 | 0 | const char *p; |
2501 | 0 | char ns_str[10] = {}; |
2502 | 0 | int set = 0; |
2503 | |
|
2504 | 0 | if (local) |
2505 | 0 | get_localtime(curr_date, &tm); |
2506 | 0 | else |
2507 | 0 | get_gmtime(curr_date, &tm); |
2508 | | |
2509 | | |
2510 | | /* we need to iterate in order to replace all the %N in the string */ |
2511 | |
|
2512 | 0 | p = format; |
2513 | 0 | while (*p) { |
2514 | 0 | const char *needle; |
2515 | 0 | int skip = 0; |
2516 | 0 | int cpy = 0; |
2517 | 0 | int width = 0; |
2518 | | |
2519 | | /* look for the next %N onversion specifier */ |
2520 | 0 | if (!(needle = lookup_convspec_N(p, &skip, &width))) |
2521 | 0 | break; |
2522 | | |
2523 | 0 | if (width > 9) /* we don't handle more that 9 */ |
2524 | 0 | width = 9; |
2525 | 0 | cpy = needle - p; |
2526 | |
|
2527 | 0 | if (!tmp_format) |
2528 | 0 | tmp_format = alloc_trash_chunk(); |
2529 | 0 | if (!tmp_format) |
2530 | 0 | goto error; |
2531 | | |
2532 | 0 | if (set != 9) /* if the snprintf wasn't done yet */ |
2533 | 0 | set = snprintf(ns_str, sizeof(ns_str), "%.9llu", (unsigned long long)ns); |
2534 | |
|
2535 | 0 | if (chunk_istcat(tmp_format, ist2(p, cpy)) == 0) /* copy before the %N */ |
2536 | 0 | goto error; |
2537 | 0 | if (chunk_istcat(tmp_format, ist2(ns_str, width)) == 0) /* copy the %N result with the right precision */ |
2538 | 0 | goto error; |
2539 | | |
2540 | 0 | p += skip + cpy; /* skip the %N */ |
2541 | 0 | } |
2542 | | |
2543 | | |
2544 | 0 | if (tmp_format) { /* %N was found */ |
2545 | 0 | if (chunk_strcat(tmp_format, p) == 0) /* copy the end of the string if needed or just the \0 */ |
2546 | 0 | goto error; |
2547 | 0 | res = get_trash_chunk(); |
2548 | 0 | res->data = strftime(res->area, res->size, tmp_format->area , &tm); |
2549 | 0 | } else { |
2550 | 0 | res = get_trash_chunk(); |
2551 | 0 | res->data = strftime(res->area, res->size, format, &tm); |
2552 | 0 | } |
2553 | | |
2554 | 0 | error: |
2555 | 0 | free_trash_chunk(tmp_format); |
2556 | 0 | return res; |
2557 | 0 | } |
2558 | | |
2559 | | |
2560 | | |
2561 | | /* |
2562 | | * same as sample_conv_ltime but input is us and %N is supported |
2563 | | */ |
2564 | | static int sample_conv_us_ltime(const struct arg *args, struct sample *smp, void *private) |
2565 | 0 | { |
2566 | 0 | struct buffer *temp; |
2567 | 0 | time_t curr_date = smp->data.u.sint / 1000000; /* convert us to s */ |
2568 | 0 | uint64_t ns = (smp->data.u.sint % 1000000) * 1000; /* us part to ns */ |
2569 | | |
2570 | | /* add offset */ |
2571 | 0 | if (args[1].type == ARGT_SINT) |
2572 | 0 | curr_date += args[1].data.sint; |
2573 | |
|
2574 | 0 | temp = conv_time_common(args[0].data.str.area, curr_date, ns, 1); |
2575 | 0 | smp->data.u.str = *temp; |
2576 | 0 | smp->data.type = SMP_T_STR; |
2577 | 0 | return 1; |
2578 | 0 | } |
2579 | | |
2580 | | /* |
2581 | | * same as sample_conv_ltime but input is ms and %N is supported |
2582 | | */ |
2583 | | static int sample_conv_ms_ltime(const struct arg *args, struct sample *smp, void *private) |
2584 | 0 | { |
2585 | 0 | struct buffer *temp; |
2586 | 0 | time_t curr_date = smp->data.u.sint / 1000; /* convert ms to s */ |
2587 | 0 | uint64_t ns = (smp->data.u.sint % 1000) * 1000000; /* ms part to ns */ |
2588 | | |
2589 | | /* add offset */ |
2590 | 0 | if (args[1].type == ARGT_SINT) |
2591 | 0 | curr_date += args[1].data.sint; |
2592 | |
|
2593 | 0 | temp = conv_time_common(args[0].data.str.area, curr_date, ns, 1); |
2594 | 0 | smp->data.u.str = *temp; |
2595 | 0 | smp->data.type = SMP_T_STR; |
2596 | 0 | return 1; |
2597 | 0 | } |
2598 | | |
2599 | | |
2600 | | /* takes an UINT value on input supposed to represent the time since EPOCH, |
2601 | | * adds an optional offset found in args[1] and emits a string representing |
2602 | | * the local time in the format specified in args[1] using strftime(). |
2603 | | */ |
2604 | | static int sample_conv_ltime(const struct arg *args, struct sample *smp, void *private) |
2605 | 0 | { |
2606 | 0 | struct buffer *temp; |
2607 | | /* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */ |
2608 | 0 | time_t curr_date = smp->data.u.sint & 0x007fffffffffffffLL; |
2609 | 0 | struct tm tm; |
2610 | | |
2611 | | /* add offset */ |
2612 | 0 | if (args[1].type == ARGT_SINT) |
2613 | 0 | curr_date += args[1].data.sint; |
2614 | |
|
2615 | 0 | get_localtime(curr_date, &tm); |
2616 | |
|
2617 | 0 | temp = get_trash_chunk(); |
2618 | 0 | temp->data = strftime(temp->area, temp->size, args[0].data.str.area, &tm); |
2619 | 0 | smp->data.u.str = *temp; |
2620 | 0 | smp->data.type = SMP_T_STR; |
2621 | 0 | return 1; |
2622 | 0 | } |
2623 | | |
2624 | | /* hashes the binary input into a 32-bit unsigned int */ |
2625 | | static int sample_conv_sdbm(const struct arg *arg_p, struct sample *smp, void *private) |
2626 | 0 | { |
2627 | 0 | smp->data.u.sint = hash_sdbm(smp->data.u.str.area, |
2628 | 0 | smp->data.u.str.data); |
2629 | 0 | if (arg_p->data.sint) |
2630 | 0 | smp->data.u.sint = full_hash(smp->data.u.sint); |
2631 | 0 | smp->data.type = SMP_T_SINT; |
2632 | 0 | return 1; |
2633 | 0 | } |
2634 | | |
2635 | | /* |
2636 | | * same as sample_conv_utime but input is us and %N is supported |
2637 | | */ |
2638 | | static int sample_conv_us_utime(const struct arg *args, struct sample *smp, void *private) |
2639 | 0 | { |
2640 | 0 | struct buffer *temp; |
2641 | 0 | time_t curr_date = smp->data.u.sint / 1000000; /* convert us to s */ |
2642 | 0 | uint64_t ns = (smp->data.u.sint % 1000000) * 1000; /* us part to ns */ |
2643 | | |
2644 | | /* add offset */ |
2645 | 0 | if (args[1].type == ARGT_SINT) |
2646 | 0 | curr_date += args[1].data.sint; |
2647 | |
|
2648 | 0 | temp = conv_time_common(args[0].data.str.area, curr_date, ns, 0); |
2649 | 0 | smp->data.u.str = *temp; |
2650 | 0 | smp->data.type = SMP_T_STR; |
2651 | 0 | return 1; |
2652 | 0 | } |
2653 | | |
2654 | | /* |
2655 | | * same as sample_conv_utime but input is ms and %N is supported |
2656 | | */ |
2657 | | static int sample_conv_ms_utime(const struct arg *args, struct sample *smp, void *private) |
2658 | 0 | { |
2659 | 0 | struct buffer *temp; |
2660 | 0 | time_t curr_date = smp->data.u.sint / 1000; /* convert ms to s */ |
2661 | 0 | uint64_t ns = (smp->data.u.sint % 1000) * 1000000; /* ms part to ns */ |
2662 | | |
2663 | | /* add offset */ |
2664 | 0 | if (args[1].type == ARGT_SINT) |
2665 | 0 | curr_date += args[1].data.sint; |
2666 | |
|
2667 | 0 | temp = conv_time_common(args[0].data.str.area, curr_date, ns, 0); |
2668 | 0 | smp->data.u.str = *temp; |
2669 | 0 | smp->data.type = SMP_T_STR; |
2670 | 0 | return 1; |
2671 | 0 | } |
2672 | | |
2673 | | /* takes an UINT value on input supposed to represent the time since EPOCH, |
2674 | | * adds an optional offset found in args[1] and emits a string representing |
2675 | | * the UTC date in the format specified in args[1] using strftime(). |
2676 | | */ |
2677 | | static int sample_conv_utime(const struct arg *args, struct sample *smp, void *private) |
2678 | 0 | { |
2679 | 0 | struct buffer *temp; |
2680 | | /* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */ |
2681 | 0 | time_t curr_date = smp->data.u.sint & 0x007fffffffffffffLL; |
2682 | 0 | struct tm tm; |
2683 | | |
2684 | | /* add offset */ |
2685 | 0 | if (args[1].type == ARGT_SINT) |
2686 | 0 | curr_date += args[1].data.sint; |
2687 | |
|
2688 | 0 | get_gmtime(curr_date, &tm); |
2689 | |
|
2690 | 0 | temp = get_trash_chunk(); |
2691 | 0 | temp->data = strftime(temp->area, temp->size, args[0].data.str.area, &tm); |
2692 | 0 | smp->data.u.str = *temp; |
2693 | 0 | smp->data.type = SMP_T_STR; |
2694 | 0 | return 1; |
2695 | 0 | } |
2696 | | |
2697 | | /* hashes the binary input into a 32-bit unsigned int */ |
2698 | | static int sample_conv_wt6(const struct arg *arg_p, struct sample *smp, void *private) |
2699 | 0 | { |
2700 | 0 | smp->data.u.sint = hash_wt6(smp->data.u.str.area, |
2701 | 0 | smp->data.u.str.data); |
2702 | 0 | if (arg_p->data.sint) |
2703 | 0 | smp->data.u.sint = full_hash(smp->data.u.sint); |
2704 | 0 | smp->data.type = SMP_T_SINT; |
2705 | 0 | return 1; |
2706 | 0 | } |
2707 | | |
2708 | | /* hashes the binary input into a 32-bit unsigned int using xxh. |
2709 | | * The seed of the hash defaults to 0 but can be changd in argument 1. |
2710 | | */ |
2711 | | static int sample_conv_xxh32(const struct arg *arg_p, struct sample *smp, void *private) |
2712 | 0 | { |
2713 | 0 | unsigned int seed; |
2714 | |
|
2715 | 0 | if (arg_p->data.sint) |
2716 | 0 | seed = arg_p->data.sint; |
2717 | 0 | else |
2718 | 0 | seed = 0; |
2719 | 0 | smp->data.u.sint = XXH32(smp->data.u.str.area, smp->data.u.str.data, |
2720 | 0 | seed); |
2721 | 0 | smp->data.type = SMP_T_SINT; |
2722 | 0 | return 1; |
2723 | 0 | } |
2724 | | |
2725 | | /* hashes the binary input into a 64-bit unsigned int using xxh. |
2726 | | * In fact, the function returns a 64 bit unsigned, but the sample |
2727 | | * storage of haproxy only proposes 64-bits signed, so the value is |
2728 | | * cast as signed. This cast doesn't impact the hash repartition. |
2729 | | * The seed of the hash defaults to 0 but can be changd in argument 1. |
2730 | | */ |
2731 | | static int sample_conv_xxh64(const struct arg *arg_p, struct sample *smp, void *private) |
2732 | 0 | { |
2733 | 0 | unsigned long long int seed; |
2734 | |
|
2735 | 0 | if (arg_p->data.sint) |
2736 | 0 | seed = (unsigned long long int)arg_p->data.sint; |
2737 | 0 | else |
2738 | 0 | seed = 0; |
2739 | 0 | smp->data.u.sint = (long long int)XXH64(smp->data.u.str.area, |
2740 | 0 | smp->data.u.str.data, seed); |
2741 | 0 | smp->data.type = SMP_T_SINT; |
2742 | 0 | return 1; |
2743 | 0 | } |
2744 | | |
2745 | | static int sample_conv_xxh3(const struct arg *arg_p, struct sample *smp, void *private) |
2746 | 0 | { |
2747 | 0 | unsigned long long int seed; |
2748 | |
|
2749 | 0 | if (arg_p->data.sint) |
2750 | 0 | seed = (unsigned long long int)arg_p->data.sint; |
2751 | 0 | else |
2752 | 0 | seed = 0; |
2753 | 0 | smp->data.u.sint = (long long int)XXH3(smp->data.u.str.area, |
2754 | 0 | smp->data.u.str.data, seed); |
2755 | 0 | smp->data.type = SMP_T_SINT; |
2756 | 0 | return 1; |
2757 | 0 | } |
2758 | | |
2759 | | /* hashes the binary input into a 32-bit unsigned int */ |
2760 | | static int sample_conv_crc32(const struct arg *arg_p, struct sample *smp, void *private) |
2761 | 0 | { |
2762 | 0 | smp->data.u.sint = hash_crc32(smp->data.u.str.area, |
2763 | 0 | smp->data.u.str.data); |
2764 | 0 | if (arg_p->data.sint) |
2765 | 0 | smp->data.u.sint = full_hash(smp->data.u.sint); |
2766 | 0 | smp->data.type = SMP_T_SINT; |
2767 | 0 | return 1; |
2768 | 0 | } |
2769 | | |
2770 | | /* hashes the binary input into crc32c (RFC4960, Appendix B [8].) */ |
2771 | | static int sample_conv_crc32c(const struct arg *arg_p, struct sample *smp, void *private) |
2772 | 0 | { |
2773 | 0 | smp->data.u.sint = hash_crc32c(smp->data.u.str.area, |
2774 | 0 | smp->data.u.str.data); |
2775 | 0 | if (arg_p->data.sint) |
2776 | 0 | smp->data.u.sint = full_hash(smp->data.u.sint); |
2777 | 0 | smp->data.type = SMP_T_SINT; |
2778 | 0 | return 1; |
2779 | 0 | } |
2780 | | |
2781 | | /* This function escape special json characters. The returned string can be |
2782 | | * safely set between two '"' and used as json string. The json string is |
2783 | | * defined like this: |
2784 | | * |
2785 | | * any Unicode character except '"' or '\' or control character |
2786 | | * \", \\, \/, \b, \f, \n, \r, \t, \u + four-hex-digits |
2787 | | * |
2788 | | * The enum input_type contain all the allowed mode for decoding the input |
2789 | | * string. |
2790 | | */ |
2791 | | enum input_type { |
2792 | | IT_ASCII = 0, |
2793 | | IT_UTF8, |
2794 | | IT_UTF8S, |
2795 | | IT_UTF8P, |
2796 | | IT_UTF8PS, |
2797 | | }; |
2798 | | |
2799 | | static int sample_conv_json_check(struct arg *arg, struct sample_conv *conv, |
2800 | | const char *file, int line, char **err) |
2801 | 0 | { |
2802 | 0 | enum input_type type; |
2803 | |
|
2804 | 0 | if (strcmp(arg->data.str.area, "") == 0) |
2805 | 0 | type = IT_ASCII; |
2806 | 0 | else if (strcmp(arg->data.str.area, "ascii") == 0) |
2807 | 0 | type = IT_ASCII; |
2808 | 0 | else if (strcmp(arg->data.str.area, "utf8") == 0) |
2809 | 0 | type = IT_UTF8; |
2810 | 0 | else if (strcmp(arg->data.str.area, "utf8s") == 0) |
2811 | 0 | type = IT_UTF8S; |
2812 | 0 | else if (strcmp(arg->data.str.area, "utf8p") == 0) |
2813 | 0 | type = IT_UTF8P; |
2814 | 0 | else if (strcmp(arg->data.str.area, "utf8ps") == 0) |
2815 | 0 | type = IT_UTF8PS; |
2816 | 0 | else { |
2817 | 0 | memprintf(err, "Unexpected input code type. " |
2818 | 0 | "Allowed value are 'ascii', 'utf8', 'utf8s', 'utf8p' and 'utf8ps'"); |
2819 | 0 | return 0; |
2820 | 0 | } |
2821 | | |
2822 | 0 | chunk_destroy(&arg->data.str); |
2823 | 0 | arg->type = ARGT_SINT; |
2824 | 0 | arg->data.sint = type; |
2825 | 0 | return 1; |
2826 | 0 | } |
2827 | | |
2828 | | static int sample_conv_json(const struct arg *arg_p, struct sample *smp, void *private) |
2829 | 0 | { |
2830 | 0 | struct buffer *temp; |
2831 | 0 | char _str[7]; /* \u + 4 hex digit + null char for sprintf. */ |
2832 | 0 | const char *str; |
2833 | 0 | int len; |
2834 | 0 | enum input_type input_type = IT_ASCII; |
2835 | 0 | unsigned int c; |
2836 | 0 | unsigned int ret; |
2837 | 0 | char *p; |
2838 | |
|
2839 | 0 | input_type = arg_p->data.sint; |
2840 | |
|
2841 | 0 | temp = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.size); |
2842 | 0 | if (!temp) |
2843 | 0 | return 0; |
2844 | 0 | temp->data = 0; |
2845 | |
|
2846 | 0 | p = smp->data.u.str.area; |
2847 | 0 | while (p < smp->data.u.str.area + smp->data.u.str.data) { |
2848 | |
|
2849 | 0 | if (input_type == IT_ASCII) { |
2850 | | /* Read input as ASCII. */ |
2851 | 0 | c = *(unsigned char *)p; |
2852 | 0 | p++; |
2853 | 0 | } |
2854 | 0 | else { |
2855 | | /* Read input as UTF8. */ |
2856 | 0 | ret = utf8_next(p, |
2857 | 0 | smp->data.u.str.data - ( p - smp->data.u.str.area), |
2858 | 0 | &c); |
2859 | 0 | p += utf8_return_length(ret); |
2860 | |
|
2861 | 0 | if (input_type == IT_UTF8 && utf8_return_code(ret) != UTF8_CODE_OK) |
2862 | 0 | return 0; |
2863 | 0 | if (input_type == IT_UTF8S && utf8_return_code(ret) != UTF8_CODE_OK) |
2864 | 0 | continue; |
2865 | 0 | if (input_type == IT_UTF8P && utf8_return_code(ret) & (UTF8_CODE_INVRANGE|UTF8_CODE_BADSEQ)) |
2866 | 0 | return 0; |
2867 | 0 | if (input_type == IT_UTF8PS && utf8_return_code(ret) & (UTF8_CODE_INVRANGE|UTF8_CODE_BADSEQ)) |
2868 | 0 | continue; |
2869 | | |
2870 | | /* Check too big values. */ |
2871 | 0 | if ((unsigned int)c > 0xffff) { |
2872 | 0 | if (input_type == IT_UTF8 || input_type == IT_UTF8P) |
2873 | 0 | return 0; |
2874 | 0 | continue; |
2875 | 0 | } |
2876 | 0 | } |
2877 | | |
2878 | | /* Convert character. */ |
2879 | 0 | if (c == '"') { |
2880 | 0 | len = 2; |
2881 | 0 | str = "\\\""; |
2882 | 0 | } |
2883 | 0 | else if (c == '\\') { |
2884 | 0 | len = 2; |
2885 | 0 | str = "\\\\"; |
2886 | 0 | } |
2887 | 0 | else if (c == '/') { |
2888 | 0 | len = 2; |
2889 | 0 | str = "\\/"; |
2890 | 0 | } |
2891 | 0 | else if (c == '\b') { |
2892 | 0 | len = 2; |
2893 | 0 | str = "\\b"; |
2894 | 0 | } |
2895 | 0 | else if (c == '\f') { |
2896 | 0 | len = 2; |
2897 | 0 | str = "\\f"; |
2898 | 0 | } |
2899 | 0 | else if (c == '\r') { |
2900 | 0 | len = 2; |
2901 | 0 | str = "\\r"; |
2902 | 0 | } |
2903 | 0 | else if (c == '\n') { |
2904 | 0 | len = 2; |
2905 | 0 | str = "\\n"; |
2906 | 0 | } |
2907 | 0 | else if (c == '\t') { |
2908 | 0 | len = 2; |
2909 | 0 | str = "\\t"; |
2910 | 0 | } |
2911 | 0 | else if (c > 0xff || !isprint((unsigned char)c)) { |
2912 | | /* isprint generate a segfault if c is too big. The man says that |
2913 | | * c must have the value of an unsigned char or EOF. |
2914 | | */ |
2915 | 0 | len = 6; |
2916 | 0 | _str[0] = '\\'; |
2917 | 0 | _str[1] = 'u'; |
2918 | 0 | snprintf(&_str[2], 5, "%04x", (unsigned short)c); |
2919 | 0 | str = _str; |
2920 | 0 | } |
2921 | 0 | else { |
2922 | 0 | len = 1; |
2923 | 0 | _str[0] = c; |
2924 | 0 | str = _str; |
2925 | 0 | } |
2926 | | |
2927 | | /* Check length */ |
2928 | 0 | if (temp->data + len > temp->size) |
2929 | 0 | return 0; |
2930 | | |
2931 | | /* Copy string. */ |
2932 | 0 | memcpy(temp->area + temp->data, str, len); |
2933 | 0 | temp->data += len; |
2934 | 0 | } |
2935 | | |
2936 | 0 | smp->flags &= ~SMP_F_CONST; |
2937 | 0 | smp->data.u.str = *temp; |
2938 | 0 | smp->data.type = SMP_T_STR; |
2939 | |
|
2940 | 0 | return 1; |
2941 | 0 | } |
2942 | | |
2943 | | /* This sample function is designed to extract some bytes from an input buffer. |
2944 | | * First arg is the offset. |
2945 | | * Optional second arg is the length to truncate */ |
2946 | | static int sample_conv_bytes(const struct arg *arg_p, struct sample *smp, void *private) |
2947 | 0 | { |
2948 | 0 | struct sample smp_arg0, smp_arg1; |
2949 | 0 | long long start_idx, length; |
2950 | | |
2951 | | // determine the start_idx and length of the output |
2952 | 0 | smp_set_owner(&smp_arg0, smp->px, smp->sess, smp->strm, smp->opt); |
2953 | 0 | if (!sample_conv_var2smp_sint(&arg_p[0], &smp_arg0) || smp_arg0.data.u.sint < 0) { |
2954 | | /* invalid or negative value */ |
2955 | 0 | goto fail; |
2956 | 0 | } |
2957 | | |
2958 | 0 | if (smp_arg0.data.u.sint >= smp->data.u.str.data) { |
2959 | | // arg0 >= the input length |
2960 | 0 | if (smp->opt & SMP_OPT_FINAL) { |
2961 | | // empty output value on final smp |
2962 | 0 | smp->data.u.str.data = 0; |
2963 | 0 | goto end; |
2964 | 0 | } |
2965 | 0 | goto wait; |
2966 | 0 | } |
2967 | 0 | start_idx = smp_arg0.data.u.sint; |
2968 | | |
2969 | | // length comes from arg1 if present, otherwise it's the remaining length |
2970 | 0 | length = smp->data.u.str.data - start_idx; |
2971 | 0 | if (arg_p[1].type != ARGT_STOP) { |
2972 | 0 | smp_set_owner(&smp_arg1, smp->px, smp->sess, smp->strm, smp->opt); |
2973 | 0 | if (!sample_conv_var2smp_sint(&arg_p[1], &smp_arg1) || smp_arg1.data.u.sint < 0) { |
2974 | | // invalid or negative value |
2975 | 0 | goto fail; |
2976 | 0 | } |
2977 | | |
2978 | 0 | if (smp_arg1.data.u.sint > (smp->data.u.str.data - start_idx)) { |
2979 | | // arg1 value is greater than the remaining length |
2980 | 0 | if (smp->opt & SMP_OPT_FINAL) { |
2981 | | // truncate to remaining length |
2982 | 0 | length = smp->data.u.str.data - start_idx; |
2983 | 0 | goto end; |
2984 | 0 | } |
2985 | 0 | goto wait; |
2986 | 0 | } |
2987 | 0 | length = smp_arg1.data.u.sint; |
2988 | 0 | } |
2989 | | |
2990 | | // update the output using the start_idx and length |
2991 | 0 | smp->data.u.str.area += start_idx; |
2992 | 0 | smp->data.u.str.data = length; |
2993 | |
|
2994 | 0 | end: |
2995 | 0 | return 1; |
2996 | | |
2997 | 0 | fail: |
2998 | 0 | smp->flags &= ~SMP_F_MAY_CHANGE; |
2999 | 0 | wait: |
3000 | 0 | smp->data.u.str.data = 0; |
3001 | 0 | return 0; |
3002 | 0 | } |
3003 | | |
3004 | | static int sample_conv_field_check(struct arg *args, struct sample_conv *conv, |
3005 | | const char *file, int line, char **err) |
3006 | 0 | { |
3007 | 0 | struct arg *arg = args; |
3008 | |
|
3009 | 0 | if (arg->type != ARGT_SINT) { |
3010 | 0 | memprintf(err, "Unexpected arg type"); |
3011 | 0 | return 0; |
3012 | 0 | } |
3013 | | |
3014 | 0 | if (!arg->data.sint) { |
3015 | 0 | memprintf(err, "Unexpected value 0 for index"); |
3016 | 0 | return 0; |
3017 | 0 | } |
3018 | | |
3019 | 0 | arg++; |
3020 | |
|
3021 | 0 | if (arg->type != ARGT_STR) { |
3022 | 0 | memprintf(err, "Unexpected arg type"); |
3023 | 0 | return 0; |
3024 | 0 | } |
3025 | | |
3026 | 0 | if (!arg->data.str.data) { |
3027 | 0 | memprintf(err, "Empty separators list"); |
3028 | 0 | return 0; |
3029 | 0 | } |
3030 | | |
3031 | 0 | return 1; |
3032 | 0 | } |
3033 | | |
3034 | | /* This sample function is designed to a return selected part of a string (field). |
3035 | | * First arg is the index of the field (start at 1) |
3036 | | * Second arg is a char list of separators (type string) |
3037 | | */ |
3038 | | static int sample_conv_field(const struct arg *arg_p, struct sample *smp, void *private) |
3039 | 0 | { |
3040 | 0 | int field; |
3041 | 0 | char *start, *end; |
3042 | 0 | int i; |
3043 | 0 | int count = (arg_p[2].type == ARGT_SINT) ? arg_p[2].data.sint : 1; |
3044 | |
|
3045 | 0 | if (!arg_p[0].data.sint) |
3046 | 0 | return 0; |
3047 | | |
3048 | 0 | if (arg_p[0].data.sint < 0) { |
3049 | 0 | field = -1; |
3050 | 0 | end = start = smp->data.u.str.area + smp->data.u.str.data; |
3051 | 0 | while (start > smp->data.u.str.area) { |
3052 | 0 | for (i = 0 ; i < arg_p[1].data.str.data; i++) { |
3053 | 0 | if (*(start-1) == arg_p[1].data.str.area[i]) { |
3054 | 0 | if (field == arg_p[0].data.sint) { |
3055 | 0 | if (count == 1) |
3056 | 0 | goto found; |
3057 | 0 | else if (count > 1) |
3058 | 0 | count--; |
3059 | 0 | } else { |
3060 | 0 | end = start-1; |
3061 | 0 | field--; |
3062 | 0 | } |
3063 | 0 | break; |
3064 | 0 | } |
3065 | 0 | } |
3066 | 0 | start--; |
3067 | 0 | } |
3068 | 0 | } else { |
3069 | 0 | field = 1; |
3070 | 0 | end = start = smp->data.u.str.area; |
3071 | 0 | while (end - smp->data.u.str.area < smp->data.u.str.data) { |
3072 | 0 | for (i = 0 ; i < arg_p[1].data.str.data; i++) { |
3073 | 0 | if (*end == arg_p[1].data.str.area[i]) { |
3074 | 0 | if (field == arg_p[0].data.sint) { |
3075 | 0 | if (count == 1) |
3076 | 0 | goto found; |
3077 | 0 | else if (count > 1) |
3078 | 0 | count--; |
3079 | 0 | } else { |
3080 | 0 | start = end+1; |
3081 | 0 | field++; |
3082 | 0 | } |
3083 | 0 | break; |
3084 | 0 | } |
3085 | 0 | } |
3086 | 0 | end++; |
3087 | 0 | } |
3088 | 0 | } |
3089 | | |
3090 | | /* Field not found */ |
3091 | 0 | if (field != arg_p[0].data.sint) { |
3092 | 0 | smp->data.u.str.data = 0; |
3093 | 0 | return 0; |
3094 | 0 | } |
3095 | 0 | found: |
3096 | 0 | smp->data.u.str.data = end - start; |
3097 | | /* If ret string is len 0, no need to |
3098 | | change pointers or to update size */ |
3099 | 0 | if (!smp->data.u.str.data) |
3100 | 0 | return 1; |
3101 | | |
3102 | | /* Compute remaining size if needed |
3103 | | Note: smp->data.u.str.size cannot be set to 0 */ |
3104 | 0 | if (smp->data.u.str.size) |
3105 | 0 | smp->data.u.str.size -= start - smp->data.u.str.area; |
3106 | |
|
3107 | 0 | smp->data.u.str.area = start; |
3108 | |
|
3109 | 0 | return 1; |
3110 | 0 | } |
3111 | | |
3112 | | /* This sample function is designed to return a word from a string. |
3113 | | * First arg is the index of the word (start at 1) |
3114 | | * Second arg is a char list of words separators (type string) |
3115 | | */ |
3116 | | static int sample_conv_word(const struct arg *arg_p, struct sample *smp, void *private) |
3117 | 0 | { |
3118 | 0 | int word; |
3119 | 0 | char *start, *end; |
3120 | 0 | int i, issep, inword; |
3121 | 0 | int count = (arg_p[2].type == ARGT_SINT) ? arg_p[2].data.sint : 1; |
3122 | |
|
3123 | 0 | if (!arg_p[0].data.sint) |
3124 | 0 | return 0; |
3125 | | |
3126 | 0 | word = 0; |
3127 | 0 | inword = 0; |
3128 | 0 | if (arg_p[0].data.sint < 0) { |
3129 | 0 | end = start = smp->data.u.str.area + smp->data.u.str.data; |
3130 | 0 | while (start > smp->data.u.str.area) { |
3131 | 0 | issep = 0; |
3132 | 0 | for (i = 0 ; i < arg_p[1].data.str.data; i++) { |
3133 | 0 | if (*(start-1) == arg_p[1].data.str.area[i]) { |
3134 | 0 | issep = 1; |
3135 | 0 | break; |
3136 | 0 | } |
3137 | 0 | } |
3138 | 0 | if (!inword) { |
3139 | 0 | if (!issep) { |
3140 | 0 | if (word != arg_p[0].data.sint) { |
3141 | 0 | word--; |
3142 | 0 | end = start; |
3143 | 0 | } |
3144 | 0 | inword = 1; |
3145 | 0 | } |
3146 | 0 | } |
3147 | 0 | else if (issep) { |
3148 | 0 | if (word == arg_p[0].data.sint) { |
3149 | 0 | if (count == 1) |
3150 | 0 | goto found; |
3151 | 0 | else if (count > 1) |
3152 | 0 | count--; |
3153 | 0 | } |
3154 | 0 | inword = 0; |
3155 | 0 | } |
3156 | 0 | start--; |
3157 | 0 | } |
3158 | 0 | } else { |
3159 | 0 | end = start = smp->data.u.str.area; |
3160 | 0 | while (end - smp->data.u.str.area < smp->data.u.str.data) { |
3161 | 0 | issep = 0; |
3162 | 0 | for (i = 0 ; i < arg_p[1].data.str.data; i++) { |
3163 | 0 | if (*end == arg_p[1].data.str.area[i]) { |
3164 | 0 | issep = 1; |
3165 | 0 | break; |
3166 | 0 | } |
3167 | 0 | } |
3168 | 0 | if (!inword) { |
3169 | 0 | if (!issep) { |
3170 | 0 | if (word != arg_p[0].data.sint) { |
3171 | 0 | word++; |
3172 | 0 | start = end; |
3173 | 0 | } |
3174 | 0 | inword = 1; |
3175 | 0 | } |
3176 | 0 | } |
3177 | 0 | else if (issep) { |
3178 | 0 | if (word == arg_p[0].data.sint) { |
3179 | 0 | if (count == 1) |
3180 | 0 | goto found; |
3181 | 0 | else if (count > 1) |
3182 | 0 | count--; |
3183 | 0 | } |
3184 | 0 | inword = 0; |
3185 | 0 | } |
3186 | 0 | end++; |
3187 | 0 | } |
3188 | 0 | } |
3189 | | |
3190 | | /* Field not found */ |
3191 | 0 | if (word != arg_p[0].data.sint) { |
3192 | 0 | smp->data.u.str.data = 0; |
3193 | 0 | return 0; |
3194 | 0 | } |
3195 | 0 | found: |
3196 | 0 | smp->data.u.str.data = end - start; |
3197 | | /* If ret string is len 0, no need to |
3198 | | change pointers or to update size */ |
3199 | 0 | if (!smp->data.u.str.data) |
3200 | 0 | return 1; |
3201 | | |
3202 | | |
3203 | | /* Compute remaining size if needed |
3204 | | Note: smp->data.u.str.size cannot be set to 0 */ |
3205 | 0 | if (smp->data.u.str.size) |
3206 | 0 | smp->data.u.str.size -= start - smp->data.u.str.area; |
3207 | |
|
3208 | 0 | smp->data.u.str.area = start; |
3209 | |
|
3210 | 0 | return 1; |
3211 | 0 | } |
3212 | | |
3213 | | static int sample_conv_param_check(struct arg *arg, struct sample_conv *conv, |
3214 | | const char *file, int line, char **err) |
3215 | 0 | { |
3216 | 0 | if (arg[1].type == ARGT_STR && arg[1].data.str.data != 1) { |
3217 | 0 | memprintf(err, "Delimiter must be exactly 1 character."); |
3218 | 0 | return 0; |
3219 | 0 | } |
3220 | | |
3221 | 0 | return 1; |
3222 | 0 | } |
3223 | | |
3224 | | static int sample_conv_param(const struct arg *arg_p, struct sample *smp, void *private) |
3225 | 0 | { |
3226 | 0 | char *pos, *end, *pend, *equal; |
3227 | 0 | char delim = '&'; |
3228 | 0 | const char *name = arg_p[0].data.str.area; |
3229 | 0 | size_t name_l = arg_p[0].data.str.data; |
3230 | |
|
3231 | 0 | if (arg_p[1].type == ARGT_STR) |
3232 | 0 | delim = *arg_p[1].data.str.area; |
3233 | |
|
3234 | 0 | pos = smp->data.u.str.area; |
3235 | 0 | end = pos + smp->data.u.str.data; |
3236 | 0 | while (pos < end) { |
3237 | 0 | equal = pos + name_l; |
3238 | | /* Parameter not found */ |
3239 | 0 | if (equal > end) |
3240 | 0 | break; |
3241 | | |
3242 | 0 | if (equal == end || *equal == delim) { |
3243 | 0 | if (memcmp(pos, name, name_l) == 0) { |
3244 | | /* input contains parameter, but no value is supplied */ |
3245 | 0 | smp->data.u.str.data = 0; |
3246 | 0 | return 1; |
3247 | 0 | } |
3248 | 0 | pos = equal + 1; |
3249 | 0 | continue; |
3250 | 0 | } |
3251 | | |
3252 | 0 | if (*equal == '=' && memcmp(pos, name, name_l) == 0) { |
3253 | 0 | pos = equal + 1; |
3254 | 0 | pend = memchr(pos, delim, end - pos); |
3255 | 0 | if (pend == NULL) |
3256 | 0 | pend = end; |
3257 | |
|
3258 | 0 | if (smp->data.u.str.size) |
3259 | 0 | smp->data.u.str.size -= pos - smp->data.u.str.area; |
3260 | 0 | smp->data.u.str.area = pos; |
3261 | 0 | smp->data.u.str.data = pend - pos; |
3262 | 0 | return 1; |
3263 | 0 | } |
3264 | | /* find the next delimiter and set position to character after that */ |
3265 | 0 | pos = memchr(pos, delim, end - pos); |
3266 | 0 | if (pos == NULL) |
3267 | 0 | pos = end; |
3268 | 0 | else |
3269 | 0 | pos++; |
3270 | 0 | } |
3271 | | /* Parameter not found */ |
3272 | 0 | smp->data.u.str.data = 0; |
3273 | 0 | return 0; |
3274 | 0 | } |
3275 | | |
3276 | | static int sample_conv_regsub_check(struct arg *args, struct sample_conv *conv, |
3277 | | const char *file, int line, char **err) |
3278 | 0 | { |
3279 | 0 | struct arg *arg = args; |
3280 | 0 | char *p; |
3281 | 0 | int len; |
3282 | | |
3283 | | /* arg0 is a regex, it uses type_flag for ICASE and global match */ |
3284 | 0 | arg[0].type_flags = 0; |
3285 | |
|
3286 | 0 | if (arg[2].type != ARGT_STR) |
3287 | 0 | return 1; |
3288 | | |
3289 | 0 | p = arg[2].data.str.area; |
3290 | 0 | len = arg[2].data.str.data; |
3291 | 0 | while (len) { |
3292 | 0 | if (*p == 'i') { |
3293 | 0 | arg[0].type_flags |= ARGF_REG_ICASE; |
3294 | 0 | } |
3295 | 0 | else if (*p == 'g') { |
3296 | 0 | arg[0].type_flags |= ARGF_REG_GLOB; |
3297 | 0 | } |
3298 | 0 | else { |
3299 | 0 | memprintf(err, "invalid regex flag '%c', only 'i' and 'g' are supported", *p); |
3300 | 0 | return 0; |
3301 | 0 | } |
3302 | 0 | p++; |
3303 | 0 | len--; |
3304 | 0 | } |
3305 | 0 | return 1; |
3306 | 0 | } |
3307 | | |
3308 | | /* This sample function is designed to do the equivalent of s/match/replace/ on |
3309 | | * the input string. It applies a regex and restarts from the last matched |
3310 | | * location until nothing matches anymore. First arg is the regex to apply to |
3311 | | * the input string, second arg is the replacement expression. |
3312 | | */ |
3313 | | static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void *private) |
3314 | 0 | { |
3315 | 0 | char *start, *end; |
3316 | 0 | struct my_regex *reg = arg_p[0].data.reg; |
3317 | 0 | regmatch_t pmatch[MAX_MATCH]; |
3318 | 0 | struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); |
3319 | 0 | struct buffer *output; |
3320 | 0 | int flag, max; |
3321 | 0 | int found; |
3322 | |
|
3323 | 0 | if (!trash) |
3324 | 0 | return 0; |
3325 | 0 | start = smp->data.u.str.area; |
3326 | 0 | end = start + smp->data.u.str.data; |
3327 | |
|
3328 | 0 | flag = 0; |
3329 | 0 | while (1) { |
3330 | | /* check for last round which is used to copy remaining parts |
3331 | | * when not running in global replacement mode. |
3332 | | */ |
3333 | 0 | found = 0; |
3334 | 0 | if ((arg_p[0].type_flags & ARGF_REG_GLOB) || !(flag & REG_NOTBOL)) { |
3335 | | /* Note: we can have start == end on empty strings or at the end */ |
3336 | 0 | found = regex_exec_match2(reg, start, end - start, MAX_MATCH, pmatch, flag); |
3337 | 0 | } |
3338 | |
|
3339 | 0 | if (!found) |
3340 | 0 | pmatch[0].rm_so = end - start; |
3341 | | |
3342 | | /* copy the heading non-matching part (which may also be the tail if nothing matches) */ |
3343 | 0 | max = trash->size - trash->data; |
3344 | 0 | if (max && pmatch[0].rm_so > 0) { |
3345 | 0 | if (max > pmatch[0].rm_so) |
3346 | 0 | max = pmatch[0].rm_so; |
3347 | 0 | memcpy(trash->area + trash->data, start, max); |
3348 | 0 | trash->data += max; |
3349 | 0 | } |
3350 | |
|
3351 | 0 | if (!found) |
3352 | 0 | break; |
3353 | | |
3354 | 0 | output = alloc_trash_chunk(); |
3355 | 0 | if (!output) |
3356 | 0 | break; |
3357 | | |
3358 | 0 | max = exp_replace(output->area, output->size, start, arg_p[1].data.str.area, pmatch); |
3359 | 0 | if ((int)max < 0) { |
3360 | 0 | free_trash_chunk(output); |
3361 | 0 | break; |
3362 | 0 | } |
3363 | 0 | output->data = max; |
3364 | | |
3365 | | /* replace the matching part */ |
3366 | 0 | max = trash->size - trash->data; |
3367 | 0 | if (max) { |
3368 | 0 | if (max > output->data) |
3369 | 0 | max = output->data; |
3370 | 0 | memcpy(trash->area + trash->data, |
3371 | 0 | output->area, max); |
3372 | 0 | trash->data += max; |
3373 | 0 | } |
3374 | |
|
3375 | 0 | free_trash_chunk(output); |
3376 | | |
3377 | | /* stop here if we're done with this string */ |
3378 | 0 | if (start >= end) |
3379 | 0 | break; |
3380 | | |
3381 | | /* We have a special case for matches of length 0 (eg: "x*y*"). |
3382 | | * These ones are considered to match in front of a character, |
3383 | | * so we have to copy that character and skip to the next one. |
3384 | | */ |
3385 | 0 | if (!pmatch[0].rm_eo) { |
3386 | 0 | if (trash->data < trash->size) |
3387 | 0 | trash->area[trash->data++] = start[pmatch[0].rm_eo]; |
3388 | 0 | pmatch[0].rm_eo++; |
3389 | 0 | } |
3390 | |
|
3391 | 0 | start += pmatch[0].rm_eo; |
3392 | 0 | flag |= REG_NOTBOL; |
3393 | 0 | } |
3394 | |
|
3395 | 0 | smp->data.u.str = *trash; |
3396 | 0 | return 1; |
3397 | 0 | } |
3398 | | |
3399 | | /* This function check an operator entry. It expects a string. |
3400 | | * The string can be an integer or a variable name. |
3401 | | */ |
3402 | | static int check_operator(struct arg *args, struct sample_conv *conv, |
3403 | | const char *file, int line, char **err) |
3404 | 0 | { |
3405 | 0 | const char *str; |
3406 | 0 | const char *end; |
3407 | 0 | long long int i; |
3408 | | |
3409 | | /* Try to decode a variable. The 'err' variable is intentionnaly left |
3410 | | * NULL since the operators accept an integer as argument in which case |
3411 | | * vars_check_arg call will fail. |
3412 | | */ |
3413 | 0 | if (vars_check_arg(&args[0], NULL)) |
3414 | 0 | return 1; |
3415 | | |
3416 | | /* Try to convert an integer */ |
3417 | 0 | str = args[0].data.str.area; |
3418 | 0 | end = str + strlen(str); |
3419 | 0 | i = read_int64(&str, end); |
3420 | 0 | if (*str != '\0') { |
3421 | 0 | memprintf(err, "expects an integer or a variable name"); |
3422 | 0 | return 0; |
3423 | 0 | } |
3424 | | |
3425 | 0 | chunk_destroy(&args[0].data.str); |
3426 | 0 | args[0].type = ARGT_SINT; |
3427 | 0 | args[0].data.sint = i; |
3428 | 0 | return 1; |
3429 | 0 | } |
3430 | | |
3431 | | /* This function returns a sample struct filled with an arg content. |
3432 | | * If the arg contain an integer, the integer is returned in the |
3433 | | * sample. If the arg contains a variable descriptor, it returns the |
3434 | | * variable value. |
3435 | | * |
3436 | | * This function returns 0 if an error occurs, otherwise it returns 1. |
3437 | | */ |
3438 | | int sample_conv_var2smp_sint(const struct arg *arg, struct sample *smp) |
3439 | 0 | { |
3440 | 0 | switch (arg->type) { |
3441 | 0 | case ARGT_SINT: |
3442 | 0 | smp->data.type = SMP_T_SINT; |
3443 | 0 | smp->data.u.sint = arg->data.sint; |
3444 | 0 | return 1; |
3445 | 0 | case ARGT_VAR: |
3446 | 0 | return sample_conv_var2smp(&arg->data.var, smp, SMP_T_SINT); |
3447 | 0 | default: |
3448 | 0 | return 0; |
3449 | 0 | } |
3450 | 0 | } |
3451 | | |
3452 | | /* Takes a SINT on input, applies a binary twos complement and returns the SINT |
3453 | | * result. |
3454 | | */ |
3455 | | static int sample_conv_binary_cpl(const struct arg *arg_p, struct sample *smp, void *private) |
3456 | 0 | { |
3457 | 0 | smp->data.u.sint = ~smp->data.u.sint; |
3458 | 0 | return 1; |
3459 | 0 | } |
3460 | | |
3461 | | /* Takes a SINT on input, applies a binary "and" with the SINT directly in |
3462 | | * arg_p or in the variable described in arg_p, and returns the SINT result. |
3463 | | */ |
3464 | | static int sample_conv_binary_and(const struct arg *arg_p, struct sample *smp, void *private) |
3465 | 0 | { |
3466 | 0 | struct sample tmp; |
3467 | |
|
3468 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3469 | 0 | if (!sample_conv_var2smp_sint(arg_p, &tmp)) |
3470 | 0 | return 0; |
3471 | 0 | smp->data.u.sint &= tmp.data.u.sint; |
3472 | 0 | return 1; |
3473 | 0 | } |
3474 | | |
3475 | | /* Takes a SINT on input, applies a binary "or" with the SINT directly in |
3476 | | * arg_p or in the variable described in arg_p, and returns the SINT result. |
3477 | | */ |
3478 | | static int sample_conv_binary_or(const struct arg *arg_p, struct sample *smp, void *private) |
3479 | 0 | { |
3480 | 0 | struct sample tmp; |
3481 | |
|
3482 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3483 | 0 | if (!sample_conv_var2smp_sint(arg_p, &tmp)) |
3484 | 0 | return 0; |
3485 | 0 | smp->data.u.sint |= tmp.data.u.sint; |
3486 | 0 | return 1; |
3487 | 0 | } |
3488 | | |
3489 | | /* Takes a SINT on input, applies a binary "xor" with the SINT directly in |
3490 | | * arg_p or in the variable described in arg_p, and returns the SINT result. |
3491 | | */ |
3492 | | static int sample_conv_binary_xor(const struct arg *arg_p, struct sample *smp, void *private) |
3493 | 0 | { |
3494 | 0 | struct sample tmp; |
3495 | |
|
3496 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3497 | 0 | if (!sample_conv_var2smp_sint(arg_p, &tmp)) |
3498 | 0 | return 0; |
3499 | 0 | smp->data.u.sint ^= tmp.data.u.sint; |
3500 | 0 | return 1; |
3501 | 0 | } |
3502 | | |
3503 | | static inline long long int arith_add(long long int a, long long int b) |
3504 | 0 | { |
3505 | | /* Prevent overflow and makes capped calculus. |
3506 | | * We must ensure that the check calculus doesn't |
3507 | | * exceed the signed 64 bits limits. |
3508 | | * |
3509 | | * +----------+----------+ |
3510 | | * | a<0 | a>=0 | |
3511 | | * +------+----------+----------+ |
3512 | | * | b<0 | MIN-a>b | no check | |
3513 | | * +------+----------+----------+ |
3514 | | * | b>=0 | no check | MAX-a<b | |
3515 | | * +------+----------+----------+ |
3516 | | */ |
3517 | 0 | if ((a ^ b) >= 0) { |
3518 | | /* signs are same. */ |
3519 | 0 | if (a < 0) { |
3520 | 0 | if (LLONG_MIN - a > b) |
3521 | 0 | return LLONG_MIN; |
3522 | 0 | } |
3523 | 0 | else if (LLONG_MAX - a < b) |
3524 | 0 | return LLONG_MAX; |
3525 | 0 | } |
3526 | 0 | return a + b; |
3527 | 0 | } |
3528 | | |
3529 | | /* Takes a SINT on input, applies an arithmetic "add" with the SINT directly in |
3530 | | * arg_p or in the variable described in arg_p, and returns the SINT result. |
3531 | | */ |
3532 | | static int sample_conv_arith_add(const struct arg *arg_p, struct sample *smp, void *private) |
3533 | 0 | { |
3534 | 0 | struct sample tmp; |
3535 | |
|
3536 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3537 | 0 | if (!sample_conv_var2smp_sint(arg_p, &tmp)) |
3538 | 0 | return 0; |
3539 | 0 | smp->data.u.sint = arith_add(smp->data.u.sint, tmp.data.u.sint); |
3540 | 0 | return 1; |
3541 | 0 | } |
3542 | | |
3543 | | /* Takes a SINT on input, applies an arithmetic "sub" with the SINT directly in |
3544 | | * arg_p or in the variable described in arg_p, and returns the SINT result. |
3545 | | */ |
3546 | | static int sample_conv_arith_sub(const struct arg *arg_p, |
3547 | | struct sample *smp, void *private) |
3548 | 0 | { |
3549 | 0 | struct sample tmp; |
3550 | |
|
3551 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3552 | 0 | if (!sample_conv_var2smp_sint(arg_p, &tmp)) |
3553 | 0 | return 0; |
3554 | | |
3555 | | /* We cannot represent -LLONG_MIN because abs(LLONG_MIN) is greater |
3556 | | * than abs(LLONG_MAX). So, the following code use LLONG_MAX in place |
3557 | | * of -LLONG_MIN and correct the result. |
3558 | | */ |
3559 | 0 | if (tmp.data.u.sint == LLONG_MIN) { |
3560 | 0 | smp->data.u.sint = arith_add(smp->data.u.sint, LLONG_MAX); |
3561 | 0 | if (smp->data.u.sint < LLONG_MAX) |
3562 | 0 | smp->data.u.sint++; |
3563 | 0 | return 1; |
3564 | 0 | } |
3565 | | |
3566 | | /* standard subtraction: we use the "add" function and negate |
3567 | | * the second operand. |
3568 | | */ |
3569 | 0 | smp->data.u.sint = arith_add(smp->data.u.sint, -tmp.data.u.sint); |
3570 | 0 | return 1; |
3571 | 0 | } |
3572 | | |
3573 | | /* Takes a SINT on input, applies an arithmetic "mul" with the SINT directly in |
3574 | | * arg_p or in the variable described in arg_p, and returns the SINT result. |
3575 | | * If the result makes an overflow, then the largest possible quantity is |
3576 | | * returned. |
3577 | | */ |
3578 | | static int sample_conv_arith_mul(const struct arg *arg_p, |
3579 | | struct sample *smp, void *private) |
3580 | 0 | { |
3581 | 0 | struct sample tmp; |
3582 | 0 | long long int c; |
3583 | |
|
3584 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3585 | 0 | if (!sample_conv_var2smp_sint(arg_p, &tmp)) |
3586 | 0 | return 0; |
3587 | | |
3588 | | /* prevent divide by 0 during the check */ |
3589 | 0 | if (!smp->data.u.sint || !tmp.data.u.sint) { |
3590 | 0 | smp->data.u.sint = 0; |
3591 | 0 | return 1; |
3592 | 0 | } |
3593 | | |
3594 | | /* The multiply between LLONG_MIN and -1 returns a |
3595 | | * "floating point exception". |
3596 | | */ |
3597 | 0 | if (smp->data.u.sint == LLONG_MIN && tmp.data.u.sint == -1) { |
3598 | 0 | smp->data.u.sint = LLONG_MAX; |
3599 | 0 | return 1; |
3600 | 0 | } |
3601 | | |
3602 | | /* execute standard multiplication. */ |
3603 | 0 | c = smp->data.u.sint * tmp.data.u.sint; |
3604 | | |
3605 | | /* check for overflow and makes capped multiply. */ |
3606 | 0 | if (smp->data.u.sint != c / tmp.data.u.sint) { |
3607 | 0 | if ((smp->data.u.sint < 0) == (tmp.data.u.sint < 0)) { |
3608 | 0 | smp->data.u.sint = LLONG_MAX; |
3609 | 0 | return 1; |
3610 | 0 | } |
3611 | 0 | smp->data.u.sint = LLONG_MIN; |
3612 | 0 | return 1; |
3613 | 0 | } |
3614 | 0 | smp->data.u.sint = c; |
3615 | 0 | return 1; |
3616 | 0 | } |
3617 | | |
3618 | | /* Takes a SINT on input, applies an arithmetic "div" with the SINT directly in |
3619 | | * arg_p or in the variable described in arg_p, and returns the SINT result. |
3620 | | * If arg_p makes the result overflow, then the largest possible quantity is |
3621 | | * returned. |
3622 | | */ |
3623 | | static int sample_conv_arith_div(const struct arg *arg_p, |
3624 | | struct sample *smp, void *private) |
3625 | 0 | { |
3626 | 0 | struct sample tmp; |
3627 | |
|
3628 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3629 | 0 | if (!sample_conv_var2smp_sint(arg_p, &tmp)) |
3630 | 0 | return 0; |
3631 | | |
3632 | 0 | if (tmp.data.u.sint) { |
3633 | | /* The divide between LLONG_MIN and -1 returns a |
3634 | | * "floating point exception". |
3635 | | */ |
3636 | 0 | if (smp->data.u.sint == LLONG_MIN && tmp.data.u.sint == -1) { |
3637 | 0 | smp->data.u.sint = LLONG_MAX; |
3638 | 0 | return 1; |
3639 | 0 | } |
3640 | 0 | smp->data.u.sint /= tmp.data.u.sint; |
3641 | 0 | return 1; |
3642 | 0 | } |
3643 | 0 | smp->data.u.sint = LLONG_MAX; |
3644 | 0 | return 1; |
3645 | 0 | } |
3646 | | |
3647 | | /* Takes a SINT on input, applies an arithmetic "mod" with the SINT directly in |
3648 | | * arg_p or in the variable described in arg_p, and returns the SINT result. |
3649 | | * If arg_p makes the result overflow, then 0 is returned. |
3650 | | */ |
3651 | | static int sample_conv_arith_mod(const struct arg *arg_p, |
3652 | | struct sample *smp, void *private) |
3653 | 0 | { |
3654 | 0 | struct sample tmp; |
3655 | |
|
3656 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3657 | 0 | if (!sample_conv_var2smp_sint(arg_p, &tmp)) |
3658 | 0 | return 0; |
3659 | | |
3660 | 0 | if (tmp.data.u.sint) { |
3661 | | /* The divide between LLONG_MIN and -1 returns a |
3662 | | * "floating point exception". |
3663 | | */ |
3664 | 0 | if (smp->data.u.sint == LLONG_MIN && tmp.data.u.sint == -1) { |
3665 | 0 | smp->data.u.sint = 0; |
3666 | 0 | return 1; |
3667 | 0 | } |
3668 | 0 | smp->data.u.sint %= tmp.data.u.sint; |
3669 | 0 | return 1; |
3670 | 0 | } |
3671 | 0 | smp->data.u.sint = 0; |
3672 | 0 | return 1; |
3673 | 0 | } |
3674 | | |
3675 | | /* Takes an SINT on input, applies an arithmetic "neg" and returns the SINT |
3676 | | * result. |
3677 | | */ |
3678 | | static int sample_conv_arith_neg(const struct arg *arg_p, |
3679 | | struct sample *smp, void *private) |
3680 | 0 | { |
3681 | 0 | if (smp->data.u.sint == LLONG_MIN) |
3682 | 0 | smp->data.u.sint = LLONG_MAX; |
3683 | 0 | else |
3684 | 0 | smp->data.u.sint = -smp->data.u.sint; |
3685 | 0 | return 1; |
3686 | 0 | } |
3687 | | |
3688 | | /* Takes a SINT on input, returns true is the value is non-null, otherwise |
3689 | | * false. The output is a BOOL. |
3690 | | */ |
3691 | | static int sample_conv_arith_bool(const struct arg *arg_p, |
3692 | | struct sample *smp, void *private) |
3693 | 0 | { |
3694 | 0 | smp->data.u.sint = !!smp->data.u.sint; |
3695 | 0 | smp->data.type = SMP_T_BOOL; |
3696 | 0 | return 1; |
3697 | 0 | } |
3698 | | |
3699 | | /* Takes a SINT on input, returns false is the value is non-null, otherwise |
3700 | | * truee. The output is a BOOL. |
3701 | | */ |
3702 | | static int sample_conv_arith_not(const struct arg *arg_p, |
3703 | | struct sample *smp, void *private) |
3704 | 0 | { |
3705 | 0 | smp->data.u.sint = !smp->data.u.sint; |
3706 | 0 | smp->data.type = SMP_T_BOOL; |
3707 | 0 | return 1; |
3708 | 0 | } |
3709 | | |
3710 | | /* Takes a SINT on input, returns true is the value is odd, otherwise false. |
3711 | | * The output is a BOOL. |
3712 | | */ |
3713 | | static int sample_conv_arith_odd(const struct arg *arg_p, |
3714 | | struct sample *smp, void *private) |
3715 | 0 | { |
3716 | 0 | smp->data.u.sint = smp->data.u.sint & 1; |
3717 | 0 | smp->data.type = SMP_T_BOOL; |
3718 | 0 | return 1; |
3719 | 0 | } |
3720 | | |
3721 | | /* Takes a SINT on input, returns true is the value is even, otherwise false. |
3722 | | * The output is a BOOL. |
3723 | | */ |
3724 | | static int sample_conv_arith_even(const struct arg *arg_p, |
3725 | | struct sample *smp, void *private) |
3726 | 0 | { |
3727 | 0 | smp->data.u.sint = !(smp->data.u.sint & 1); |
3728 | 0 | smp->data.type = SMP_T_BOOL; |
3729 | 0 | return 1; |
3730 | 0 | } |
3731 | | |
3732 | | /* appends an optional const string, an optional variable contents and another |
3733 | | * optional const string to an existing string. |
3734 | | */ |
3735 | | static int sample_conv_concat(const struct arg *arg_p, struct sample *smp, void *private) |
3736 | 0 | { |
3737 | 0 | struct buffer *trash; |
3738 | 0 | struct sample tmp; |
3739 | 0 | struct ist var = IST_NULL; |
3740 | 0 | int max; |
3741 | | |
3742 | |
|
3743 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3744 | 0 | if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) && |
3745 | 0 | (sample_casts[tmp.data.type][SMP_T_STR] == c_none || |
3746 | 0 | sample_casts[tmp.data.type][SMP_T_STR](&tmp))) { |
3747 | 0 | var = ist2(b_orig(&tmp.data.u.str), b_data(&tmp.data.u.str)); |
3748 | 0 | } |
3749 | |
|
3750 | 0 | trash = alloc_best_trash_chunk(&smp->data.u.str, |
3751 | 0 | smp->data.u.str.data + arg_p[0].data.str.data + istlen(var) + arg_p[2].data.str.data); |
3752 | 0 | if (!trash) |
3753 | 0 | return 0; |
3754 | | |
3755 | 0 | trash->data = smp->data.u.str.data; |
3756 | 0 | if (trash->data > trash->size - 1) |
3757 | 0 | trash->data = trash->size - 1; |
3758 | |
|
3759 | 0 | memcpy(trash->area, smp->data.u.str.area, trash->data); |
3760 | 0 | trash->area[trash->data] = 0; |
3761 | | |
3762 | | /* append first string */ |
3763 | 0 | max = arg_p[0].data.str.data; |
3764 | 0 | if (max > trash->size - 1 - trash->data) |
3765 | 0 | max = trash->size - 1 - trash->data; |
3766 | |
|
3767 | 0 | if (max) { |
3768 | 0 | memcpy(trash->area + trash->data, arg_p[0].data.str.area, max); |
3769 | 0 | trash->data += max; |
3770 | 0 | trash->area[trash->data] = 0; |
3771 | 0 | } |
3772 | | |
3773 | | /* append second string (variable) if it's found */ |
3774 | 0 | max = istlen(var); |
3775 | 0 | if (max > trash->size - 1 - trash->data) |
3776 | 0 | max = trash->size - 1 - trash->data; |
3777 | |
|
3778 | 0 | if (max) { |
3779 | 0 | memcpy(trash->area + trash->data, istptr(var), max); |
3780 | 0 | trash->data += max; |
3781 | 0 | trash->area[trash->data] = 0; |
3782 | 0 | } |
3783 | | |
3784 | | /* append third string */ |
3785 | 0 | max = arg_p[2].data.str.data; |
3786 | 0 | if (max > trash->size - 1 - trash->data) |
3787 | 0 | max = trash->size - 1 - trash->data; |
3788 | |
|
3789 | 0 | if (max) { |
3790 | 0 | memcpy(trash->area + trash->data, arg_p[2].data.str.area, max); |
3791 | 0 | trash->data += max; |
3792 | 0 | trash->area[trash->data] = 0; |
3793 | 0 | } |
3794 | |
|
3795 | 0 | smp->data.u.str = *trash; |
3796 | 0 | smp->data.type = SMP_T_STR; |
3797 | 0 | smp_dup(smp); |
3798 | 0 | free_trash_chunk(trash); |
3799 | 0 | return 1; |
3800 | 0 | } |
3801 | | |
3802 | | /* This function checks the "concat" converter's arguments and extracts the |
3803 | | * variable name and its scope. |
3804 | | */ |
3805 | | static int smp_check_concat(struct arg *args, struct sample_conv *conv, |
3806 | | const char *file, int line, char **err) |
3807 | 0 | { |
3808 | | /* Try to decode a variable. */ |
3809 | 0 | if (args[1].data.str.data > 0 && !vars_check_arg(&args[1], NULL)) { |
3810 | 0 | memprintf(err, "failed to register variable name '%s'", |
3811 | 0 | args[1].data.str.area); |
3812 | 0 | return 0; |
3813 | 0 | } |
3814 | 0 | return 1; |
3815 | 0 | } |
3816 | | |
3817 | | /* Append delimiter (only to a non empty input) followed by the optional |
3818 | | * variable contents concatenated with the optional suffix. |
3819 | | */ |
3820 | | static int sample_conv_add_item(const struct arg *arg_p, struct sample *smp, void *private) |
3821 | 0 | { |
3822 | 0 | struct buffer *tmpbuf; |
3823 | 0 | struct sample tmp; |
3824 | 0 | struct ist var = IST_NULL; |
3825 | 0 | size_t max; |
3826 | | |
3827 | | /* Check if variable is found and we can turn into a string. */ |
3828 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3829 | 0 | if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) && |
3830 | 0 | (sample_casts[tmp.data.type][SMP_T_STR] == c_none || |
3831 | 0 | sample_casts[tmp.data.type][SMP_T_STR](&tmp))) { |
3832 | 0 | var = ist2(b_orig(&tmp.data.u.str), b_data(&tmp.data.u.str)); |
3833 | 0 | } |
3834 | |
|
3835 | 0 | tmpbuf = alloc_best_trash_chunk(&smp->data.u.str, |
3836 | 0 | smp->data.u.str.data + arg_p[0].data.str.data + istlen(var) + arg_p[2].data.str.data); |
3837 | 0 | if (!tmpbuf) |
3838 | 0 | return 0; |
3839 | | |
3840 | 0 | tmpbuf->data = smp->data.u.str.data; |
3841 | 0 | if (tmpbuf->data > tmpbuf->size - 1) |
3842 | 0 | tmpbuf->data = tmpbuf->size - 1; |
3843 | |
|
3844 | 0 | memcpy(tmpbuf->area, smp->data.u.str.area, tmpbuf->data); |
3845 | 0 | tmpbuf->area[tmpbuf->data] = 0; |
3846 | | |
3847 | | /* Append delimiter only if input is not empty and either |
3848 | | * the variable or the suffix are not empty |
3849 | | */ |
3850 | 0 | if (smp->data.u.str.data && ((istlen(var) && tmp.data.u.str.data) || arg_p[2].data.str.data)) { |
3851 | 0 | max = arg_p[0].data.str.data; |
3852 | 0 | if (max > tmpbuf->size - 1 - tmpbuf->data) |
3853 | 0 | max = tmpbuf->size - 1 - tmpbuf->data; |
3854 | |
|
3855 | 0 | if (max) { |
3856 | 0 | memcpy(tmpbuf->area + tmpbuf->data, arg_p[0].data.str.area, max); |
3857 | 0 | tmpbuf->data += max; |
3858 | 0 | tmpbuf->area[tmpbuf->data] = 0; |
3859 | 0 | } |
3860 | 0 | } |
3861 | | |
3862 | | /* Append variable contents if variable is found and turned into string. */ |
3863 | 0 | if (istlen(var)) { |
3864 | 0 | max = istlen(var); |
3865 | 0 | if (max > tmpbuf->size - 1 - tmpbuf->data) |
3866 | 0 | max = tmpbuf->size - 1 - tmpbuf->data; |
3867 | |
|
3868 | 0 | if (max) { |
3869 | 0 | memcpy(tmpbuf->area + tmpbuf->data, istptr(var), max); |
3870 | 0 | tmpbuf->data += max; |
3871 | 0 | tmpbuf->area[tmpbuf->data] = 0; |
3872 | 0 | } |
3873 | 0 | } |
3874 | | |
3875 | | /* Append optional suffix. */ |
3876 | 0 | max = arg_p[2].data.str.data; |
3877 | 0 | if (max > tmpbuf->size - 1 - tmpbuf->data) |
3878 | 0 | max = tmpbuf->size - 1 - tmpbuf->data; |
3879 | |
|
3880 | 0 | if (max) { |
3881 | 0 | memcpy(tmpbuf->area + tmpbuf->data, arg_p[2].data.str.area, max); |
3882 | 0 | tmpbuf->data += max; |
3883 | 0 | tmpbuf->area[tmpbuf->data] = 0; |
3884 | 0 | } |
3885 | |
|
3886 | 0 | smp->data.u.str = *tmpbuf; |
3887 | 0 | smp->data.type = SMP_T_STR; |
3888 | 0 | smp_dup(smp); |
3889 | 0 | free_trash_chunk(tmpbuf); |
3890 | 0 | return 1; |
3891 | 0 | } |
3892 | | |
3893 | | /* Check the "add_item" converter's arguments and extracts the |
3894 | | * variable name and its scope. |
3895 | | */ |
3896 | | static int smp_check_add_item(struct arg *args, struct sample_conv *conv, |
3897 | | const char *file, int line, char **err) |
3898 | 0 | { |
3899 | | /* Try to decode a variable. */ |
3900 | 0 | if (args[1].data.str.data > 0 && !vars_check_arg(&args[1], NULL)) { |
3901 | 0 | memprintf(err, "failed to register variable name '%s'", |
3902 | 0 | args[1].data.str.area); |
3903 | 0 | return 0; |
3904 | 0 | } |
3905 | | |
3906 | 0 | if (args[1].data.str.data == 0 && args[2].data.str.data == 0) { |
3907 | 0 | memprintf(err, "one of the optional arguments has to be nonempty"); |
3908 | 0 | return 0; |
3909 | 0 | } |
3910 | | |
3911 | 0 | return 1; |
3912 | 0 | } |
3913 | | |
3914 | | /* Compares string with a variable containing a string. Return value |
3915 | | * is compatible with strcmp(3)'s return value. |
3916 | | */ |
3917 | | static int sample_conv_strcmp(const struct arg *arg_p, struct sample *smp, void *private) |
3918 | 0 | { |
3919 | 0 | struct sample tmp; |
3920 | 0 | int max, result; |
3921 | |
|
3922 | 0 | smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); |
3923 | 0 | if (arg_p[0].type != ARGT_VAR) |
3924 | 0 | return 0; |
3925 | | |
3926 | 0 | if (!sample_conv_var2smp(&arg_p[0].data.var, &tmp, SMP_T_STR)) |
3927 | 0 | return 0; |
3928 | | |
3929 | 0 | max = MIN(smp->data.u.str.data, tmp.data.u.str.data); |
3930 | 0 | result = strncmp(smp->data.u.str.area, tmp.data.u.str.area, max); |
3931 | 0 | if (result == 0) { |
3932 | 0 | if (smp->data.u.str.data != tmp.data.u.str.data) { |
3933 | 0 | if (smp->data.u.str.data < tmp.data.u.str.data) { |
3934 | 0 | result = -1; |
3935 | 0 | } |
3936 | 0 | else { |
3937 | 0 | result = 1; |
3938 | 0 | } |
3939 | 0 | } |
3940 | 0 | } |
3941 | |
|
3942 | 0 | smp->data.u.sint = result; |
3943 | 0 | smp->data.type = SMP_T_SINT; |
3944 | 0 | return 1; |
3945 | 0 | } |
3946 | | /* |
3947 | | * This converter can takes a Host header value as defined by rfc9110#section-7.2 |
3948 | | * Host = uri-host [ ":" port ] ; |
3949 | | * It returns the uri-host value in lowecase with the port stripped. |
3950 | | */ |
3951 | | static int sample_conv_host_only(const struct arg *arg_p, struct sample *smp, void *private) |
3952 | 0 | { |
3953 | | /* Working cases: hostname00, hostname00:80, 127.0.0.1, 127.0.0.1:80, [::1], [::1]:80 */ |
3954 | 0 | char *beg = smp->data.u.str.area; |
3955 | 0 | char *end = smp->data.u.str.area + smp->data.u.str.data - 1; |
3956 | 0 | char *p; |
3957 | |
|
3958 | 0 | for (p = end; p >= beg; p--) { |
3959 | 0 | if (*p == ':' || *p == ']') |
3960 | 0 | break; |
3961 | 0 | } |
3962 | |
|
3963 | 0 | if (p >= beg && *p == ':') |
3964 | 0 | smp->data.u.str.data = p - beg; |
3965 | | /* if no port part was found, the hostname is the whole string */ |
3966 | |
|
3967 | 0 | smp->data.type = SMP_T_STR; |
3968 | |
|
3969 | 0 | return sample_conv_str2lower(arg_p, smp, NULL); |
3970 | 0 | } |
3971 | | |
3972 | | /* |
3973 | | * This converter can takes a Host header value as defined by rfc9110#section-7.2 |
3974 | | * Host = uri-host [ ":" port ] ; |
3975 | | * It returns the port value as a int. |
3976 | | */ |
3977 | | static int sample_conv_port_only(const struct arg *arg_p, struct sample *smp, void *private) |
3978 | 0 | { |
3979 | | /* Working cases: hostname00, hostname00:80, 127.0.0.1, 127.0.0.1:80, [::1], [::1]:80 */ |
3980 | 0 | char *beg = smp->data.u.str.area; |
3981 | 0 | char *end = smp->data.u.str.area + smp->data.u.str.data - 1; |
3982 | 0 | char *p; |
3983 | |
|
3984 | 0 | for (p = end; p >= beg; p--) { |
3985 | 0 | if (*p == ':' || *p == ']') |
3986 | 0 | break; |
3987 | 0 | } |
3988 | |
|
3989 | 0 | smp->data.type = SMP_T_SINT; |
3990 | 0 | if (p >= beg && *p == ':' && ++p <= end) { |
3991 | 0 | smp->data.u.sint = strl2ui(p, smp->data.u.str.data + smp->data.u.str.area - p); |
3992 | 0 | } else { |
3993 | 0 | smp->data.u.sint = 0; |
3994 | 0 | } |
3995 | 0 | return 1; |
3996 | 0 | } |
3997 | | |
3998 | | |
3999 | | /* Takes a boolean as input. Returns the first argument if that boolean is true and |
4000 | | * the second argument otherwise. |
4001 | | */ |
4002 | | static int sample_conv_iif(const struct arg *arg_p, struct sample *smp, void *private) |
4003 | 0 | { |
4004 | 0 | smp->data.type = SMP_T_STR; |
4005 | 0 | smp->flags |= SMP_F_CONST; |
4006 | |
|
4007 | 0 | if (smp->data.u.sint) { |
4008 | 0 | smp->data.u.str.data = arg_p[0].data.str.data; |
4009 | 0 | smp->data.u.str.area = arg_p[0].data.str.area; |
4010 | 0 | } |
4011 | 0 | else { |
4012 | 0 | smp->data.u.str.data = arg_p[1].data.str.data; |
4013 | 0 | smp->data.u.str.area = arg_p[1].data.str.area; |
4014 | 0 | } |
4015 | |
|
4016 | 0 | return 1; |
4017 | 0 | } |
4018 | | |
4019 | | enum { |
4020 | | WHEN_COND_STOPPING, |
4021 | | WHEN_COND_NORMAL, |
4022 | | WHEN_COND_ERROR, |
4023 | | WHEN_COND_FORWARDED, |
4024 | | WHEN_COND_TOAPPLET, |
4025 | | WHEN_COND_PROCESSED, |
4026 | | WHEN_COND_ACL, |
4027 | | WHEN_COND_CONDITIONS |
4028 | | }; |
4029 | | |
4030 | | const char *when_cond_kw[WHEN_COND_CONDITIONS] = { |
4031 | | [WHEN_COND_STOPPING] = "stopping", |
4032 | | [WHEN_COND_NORMAL] = "normal", |
4033 | | [WHEN_COND_ERROR] = "error", |
4034 | | [WHEN_COND_FORWARDED] = "forwarded", |
4035 | | [WHEN_COND_TOAPPLET] = "toapplet", |
4036 | | [WHEN_COND_PROCESSED] = "processed", |
4037 | | [WHEN_COND_ACL] = "acl", |
4038 | | }; |
4039 | | |
4040 | | /* Evaluates a condition and decides whether or not to pass the input sample |
4041 | | * to the output. The purpose is to hide some info when certain conditions are |
4042 | | * (not) met. These conditions belong to a fixed list that can verify internal |
4043 | | * states (debug mode, too high load, reloading, server down, stream in error |
4044 | | * etc). The condition's sign is placed in arg_p[0].data.int. 0=direct, 1=inv. |
4045 | | * The condition keyword is in arg_p[1].data.int (WHEN_COND_*). |
4046 | | */ |
4047 | | static int sample_conv_when(const struct arg *arg_p, struct sample *smp, void *private) |
4048 | 0 | { |
4049 | 0 | struct session *sess = smp->sess; |
4050 | 0 | struct stream *strm = smp->strm; |
4051 | 0 | int neg = arg_p[0].data.sint; |
4052 | 0 | int cond = arg_p[1].data.sint; |
4053 | 0 | struct acl_sample *acl_sample; |
4054 | 0 | int ret = 0; |
4055 | |
|
4056 | 0 | switch (cond) { |
4057 | 0 | case WHEN_COND_STOPPING: |
4058 | 0 | ret = !!stopping; |
4059 | 0 | break; |
4060 | | |
4061 | 0 | case WHEN_COND_NORMAL: |
4062 | 0 | neg = !neg; |
4063 | 0 | __fallthrough; |
4064 | |
|
4065 | 0 | case WHEN_COND_ERROR: |
4066 | 0 | if (strm && |
4067 | 0 | ((strm->flags & SF_REDISP) || |
4068 | 0 | ((strm->flags & SF_ERR_MASK) > SF_ERR_LOCAL) || |
4069 | 0 | (((strm->flags & SF_ERR_MASK) == SF_ERR_NONE) && strm->conn_retries) || |
4070 | 0 | ((sess->fe->mode == PR_MODE_HTTP) && strm->txn.http && strm->txn.http->status >= 500))) |
4071 | 0 | ret = 1; |
4072 | 0 | break; |
4073 | | |
4074 | 0 | case WHEN_COND_FORWARDED: // true if forwarded to a connection |
4075 | 0 | ret = strm && !!sc_conn(strm->scb); |
4076 | 0 | break; |
4077 | | |
4078 | 0 | case WHEN_COND_TOAPPLET: // true if handled as an applet |
4079 | 0 | ret = strm && !!sc_appctx(strm->scb); |
4080 | 0 | break; |
4081 | | |
4082 | 0 | case WHEN_COND_PROCESSED: // true if forwarded or appctx |
4083 | 0 | ret = strm && (sc_conn(strm->scb) || sc_appctx(strm->scb)); |
4084 | 0 | break; |
4085 | | |
4086 | 0 | case WHEN_COND_ACL: // true if the ACL pointed to by args[2] evaluates to true |
4087 | 0 | acl_sample = arg_p[2].data.ptr; |
4088 | 0 | ret = acl_exec_cond(&acl_sample->cond, smp->px, smp->sess, strm, smp->opt) == ACL_TEST_PASS; |
4089 | 0 | break; |
4090 | 0 | } |
4091 | | |
4092 | 0 | ret = !!ret ^ !!neg; |
4093 | 0 | if (!ret) { |
4094 | | /* kill the sample */ |
4095 | 0 | return 0; |
4096 | 0 | } |
4097 | | |
4098 | | /* pass the sample as-is */ |
4099 | 0 | return 1; |
4100 | 0 | } |
4101 | | |
4102 | | /* checks and resolves the type of the argument passed to when(). |
4103 | | * It supports an optional '!' to negate the condition, followed by |
4104 | | * a keyword among the list above. Note that we're purposely declaring |
4105 | | * one extra arg because the first one will be split into two. |
4106 | | */ |
4107 | | static int check_when_cond(struct arg *args, struct sample_conv *conv, |
4108 | | const char *file, int line, char **err) |
4109 | 0 | { |
4110 | 0 | struct acl_sample *acl_sample; |
4111 | 0 | const char *kw; |
4112 | 0 | int neg = 0; |
4113 | 0 | int i; |
4114 | |
|
4115 | 0 | kw = args[0].data.str.area; |
4116 | 0 | if (*kw == '!') { |
4117 | 0 | kw++; |
4118 | 0 | neg = 1; |
4119 | 0 | } |
4120 | |
|
4121 | 0 | for (i = 0; i < WHEN_COND_CONDITIONS; i++) { |
4122 | 0 | if (strcmp(kw, when_cond_kw[i]) == 0) |
4123 | 0 | break; |
4124 | 0 | } |
4125 | |
|
4126 | 0 | if (i == WHEN_COND_CONDITIONS) { |
4127 | 0 | memprintf(err, "expects a supported keyword among {"); |
4128 | 0 | for (i = 0; i < WHEN_COND_CONDITIONS; i++) |
4129 | 0 | memprintf(err, "%s%s%s", *err, when_cond_kw[i], (i == WHEN_COND_CONDITIONS - 1) ? "}" : ","); |
4130 | 0 | memprintf(err, "%s but got '%s'", *err, kw); |
4131 | 0 | return 0; |
4132 | 0 | } |
4133 | | |
4134 | 0 | if (i == WHEN_COND_ACL) { |
4135 | 0 | if (args[1].type != ARGT_STR || !*args[1].data.str.area) { |
4136 | 0 | memprintf(err, "'acl' selector requires an extra argument with the ACL name"); |
4137 | 0 | return 0; |
4138 | 0 | } |
4139 | | |
4140 | 0 | if (!curproxy) { |
4141 | 0 | memprintf(err, "'acl' selector may only be used in the context of a proxy"); |
4142 | 0 | return 0; |
4143 | 0 | } |
4144 | | |
4145 | 0 | acl_sample = calloc(1, sizeof(struct acl_sample) + sizeof(struct acl_term)); |
4146 | 0 | if (!acl_sample) { |
4147 | 0 | memprintf(err, "not enough memory for 'acl' selector"); |
4148 | 0 | return 0; |
4149 | 0 | } |
4150 | | |
4151 | 0 | LIST_INIT(&acl_sample->suite.terms); |
4152 | 0 | LIST_INIT(&acl_sample->cond.suites); |
4153 | 0 | LIST_APPEND(&acl_sample->cond.suites, &acl_sample->suite.list); |
4154 | 0 | LIST_APPEND(&acl_sample->suite.terms, &acl_sample->terms[0].list); |
4155 | 0 | acl_sample->cond.val = ~0U; // the keyword is valid everywhere for now. |
4156 | | |
4157 | | /* build one term based on the ACL kw */ |
4158 | 0 | if (!(acl_sample->terms[0].acl = find_acl_by_name(args[1].data.str.area, &curproxy->acl)) && |
4159 | 0 | !(acl_sample->terms[0].acl = find_acl_default(args[1].data.str.area, &curproxy->acl, err, NULL, NULL, 0))) { |
4160 | 0 | memprintf(err, "ACL '%s' not found", args[1].data.str.area); |
4161 | 0 | free(acl_sample); |
4162 | 0 | return 0; |
4163 | 0 | } |
4164 | | |
4165 | 0 | acl_sample->cond.use |= acl_sample->terms[0].acl->use; |
4166 | 0 | acl_sample->cond.val &= acl_sample->terms[0].acl->val; |
4167 | |
|
4168 | 0 | args[2].type = ARGT_PTR; |
4169 | 0 | args[2].unresolved = 0; |
4170 | 0 | args[2].resolve_ptr = NULL; |
4171 | 0 | args[2].data.ptr = acl_sample; |
4172 | 0 | } |
4173 | | |
4174 | 0 | chunk_destroy(&args[0].data.str); |
4175 | 0 | if (args[1].type == ARGT_STR) |
4176 | 0 | chunk_destroy(&args[1].data.str); |
4177 | |
|
4178 | 0 | if (args[2].type == ARGT_STR) |
4179 | 0 | chunk_destroy(&args[2].data.str); |
4180 | | |
4181 | | // store condition |
4182 | 0 | args[0].type = ARGT_SINT; |
4183 | 0 | args[0].data.sint = neg; // '!' present |
4184 | | |
4185 | | // and keyword |
4186 | 0 | args[1].type = ARGT_SINT; |
4187 | 0 | args[1].data.sint = i; |
4188 | 0 | return 1; |
4189 | 0 | } |
4190 | | |
4191 | 0 | #define GRPC_MSG_COMPRESS_FLAG_SZ 1 /* 1 byte */ |
4192 | 0 | #define GRPC_MSG_LENGTH_SZ 4 /* 4 bytes */ |
4193 | 0 | #define GRPC_MSG_HEADER_SZ (GRPC_MSG_COMPRESS_FLAG_SZ + GRPC_MSG_LENGTH_SZ) |
4194 | | |
4195 | | /* |
4196 | | * Extract the field value of an input binary sample. Takes a mandatory argument: |
4197 | | * the protocol buffers field identifier (dotted notation) internally represented |
4198 | | * as an array of unsigned integers and its size. |
4199 | | * Return 1 if the field was found, 0 if not. |
4200 | | */ |
4201 | | static int sample_conv_ungrpc(const struct arg *arg_p, struct sample *smp, void *private) |
4202 | 0 | { |
4203 | 0 | unsigned char *pos; |
4204 | 0 | size_t grpc_left; |
4205 | |
|
4206 | 0 | pos = (unsigned char *)smp->data.u.str.area; |
4207 | 0 | grpc_left = smp->data.u.str.data; |
4208 | |
|
4209 | 0 | while (grpc_left > GRPC_MSG_HEADER_SZ) { |
4210 | 0 | size_t grpc_msg_len, left; |
4211 | |
|
4212 | 0 | grpc_msg_len = left = ntohl(read_u32(pos + GRPC_MSG_COMPRESS_FLAG_SZ)); |
4213 | |
|
4214 | 0 | pos += GRPC_MSG_HEADER_SZ; |
4215 | 0 | grpc_left -= GRPC_MSG_HEADER_SZ; |
4216 | |
|
4217 | 0 | if (grpc_left < left) |
4218 | 0 | return 0; |
4219 | | |
4220 | 0 | if (protobuf_field_lookup(arg_p, smp, &pos, &left)) |
4221 | 0 | return 1; |
4222 | | |
4223 | 0 | grpc_left -= grpc_msg_len; |
4224 | 0 | } |
4225 | | |
4226 | 0 | return 0; |
4227 | 0 | } |
4228 | | |
4229 | | static int sample_conv_protobuf(const struct arg *arg_p, struct sample *smp, void *private) |
4230 | 0 | { |
4231 | 0 | unsigned char *pos; |
4232 | 0 | size_t left; |
4233 | |
|
4234 | 0 | pos = (unsigned char *)smp->data.u.str.area; |
4235 | 0 | left = smp->data.u.str.data; |
4236 | |
|
4237 | 0 | return protobuf_field_lookup(arg_p, smp, &pos, &left); |
4238 | 0 | } |
4239 | | |
4240 | | static int sample_conv_protobuf_check(struct arg *args, struct sample_conv *conv, |
4241 | | const char *file, int line, char **err) |
4242 | 0 | { |
4243 | 0 | if (!args[1].type) { |
4244 | 0 | args[1].type = ARGT_SINT; |
4245 | 0 | args[1].data.sint = PBUF_T_BINARY; |
4246 | 0 | } |
4247 | 0 | else { |
4248 | 0 | int pbuf_type; |
4249 | |
|
4250 | 0 | pbuf_type = protobuf_type(args[1].data.str.area); |
4251 | 0 | if (pbuf_type == -1) { |
4252 | 0 | memprintf(err, "Wrong protocol buffer type '%s'", args[1].data.str.area); |
4253 | 0 | return 0; |
4254 | 0 | } |
4255 | | |
4256 | 0 | chunk_destroy(&args[1].data.str); |
4257 | 0 | args[1].type = ARGT_SINT; |
4258 | 0 | args[1].data.sint = pbuf_type; |
4259 | 0 | } |
4260 | | |
4261 | 0 | return 1; |
4262 | 0 | } |
4263 | | |
4264 | | /* |
4265 | | * Extract the tag value of an input binary sample. Takes a mandatory argument: |
4266 | | * the FIX protocol tag identifier. |
4267 | | * Return 1 if the tag was found, 0 if not. |
4268 | | */ |
4269 | | static int sample_conv_fix_tag_value(const struct arg *arg_p, struct sample *smp, void *private) |
4270 | 0 | { |
4271 | 0 | struct ist value; |
4272 | |
|
4273 | 0 | smp->flags &= ~SMP_F_MAY_CHANGE; |
4274 | 0 | value = fix_tag_value(ist2(smp->data.u.str.area, smp->data.u.str.data), |
4275 | 0 | arg_p[0].data.sint); |
4276 | 0 | if (!istlen(value)) { |
4277 | 0 | if (isttest(value)) { |
4278 | | /* value != IST_NULL, need more data */ |
4279 | 0 | smp->flags |= SMP_F_MAY_CHANGE; |
4280 | 0 | } |
4281 | 0 | return 0; |
4282 | 0 | } |
4283 | | |
4284 | 0 | smp->data.u.str = ist2buf(value); |
4285 | 0 | smp->flags |= SMP_F_CONST; |
4286 | |
|
4287 | 0 | return 1; |
4288 | 0 | } |
4289 | | |
4290 | | /* This function checks the "fix_tag_value" converter configuration. |
4291 | | * It expects a "known" (by HAProxy) tag name or ID. |
4292 | | * Tag string names are converted to their ID counterpart because this is the |
4293 | | * format they are sent over the wire. |
4294 | | */ |
4295 | | static int sample_conv_fix_value_check(struct arg *args, struct sample_conv *conv, |
4296 | | const char *file, int line, char **err) |
4297 | 0 | { |
4298 | 0 | struct ist str; |
4299 | 0 | unsigned int tag; |
4300 | |
|
4301 | 0 | str = ist2(args[0].data.str.area, args[0].data.str.data); |
4302 | 0 | tag = fix_tagid(str); |
4303 | 0 | if (!tag) { |
4304 | 0 | memprintf(err, "Unknown FIX tag name '%s'", args[0].data.str.area); |
4305 | 0 | return 0; |
4306 | 0 | } |
4307 | | |
4308 | 0 | chunk_destroy(&args[0].data.str); |
4309 | 0 | args[0].type = ARGT_SINT; |
4310 | 0 | args[0].data.sint = tag; |
4311 | |
|
4312 | 0 | return 1; |
4313 | 0 | } |
4314 | | |
4315 | | /* |
4316 | | * Checks that a buffer contains a valid FIX message |
4317 | | * |
4318 | | * Return 1 if the check could be run, 0 if not. |
4319 | | * The result of the analyse itself is stored in <smp> as a boolean |
4320 | | */ |
4321 | | static int sample_conv_fix_is_valid(const struct arg *arg_p, struct sample *smp, void *private) |
4322 | 0 | { |
4323 | 0 | struct ist msg; |
4324 | |
|
4325 | 0 | msg = ist2(smp->data.u.str.area, smp->data.u.str.data); |
4326 | |
|
4327 | 0 | smp->flags &= ~SMP_F_MAY_CHANGE; |
4328 | 0 | switch (fix_validate_message(msg)) { |
4329 | 0 | case FIX_VALID_MESSAGE: |
4330 | 0 | smp->data.type = SMP_T_BOOL; |
4331 | 0 | smp->data.u.sint = 1; |
4332 | 0 | return 1; |
4333 | 0 | case FIX_NEED_MORE_DATA: |
4334 | 0 | smp->flags |= SMP_F_MAY_CHANGE; |
4335 | 0 | return 0; |
4336 | 0 | case FIX_INVALID_MESSAGE: |
4337 | 0 | smp->data.type = SMP_T_BOOL; |
4338 | 0 | smp->data.u.sint = 0; |
4339 | 0 | return 1; |
4340 | 0 | } |
4341 | 0 | return 0; |
4342 | 0 | } |
4343 | | |
4344 | | /* |
4345 | | * Extract the field value of an input binary sample containing an MQTT packet. |
4346 | | * Takes 2 mandatory arguments: |
4347 | | * - packet type |
4348 | | * - field name |
4349 | | * |
4350 | | * return 1 if the field was found, 0 if not. |
4351 | | */ |
4352 | | static int sample_conv_mqtt_field_value(const struct arg *arg_p, struct sample *smp, void *private) |
4353 | 0 | { |
4354 | 0 | struct ist pkt, value; |
4355 | 0 | int type, fieldname_id; |
4356 | |
|
4357 | 0 | pkt = ist2(smp->data.u.str.area, smp->data.u.str.data); |
4358 | 0 | type = arg_p[0].data.sint; |
4359 | 0 | fieldname_id = arg_p[1].data.sint; |
4360 | |
|
4361 | 0 | smp->flags &= ~SMP_F_MAY_CHANGE; |
4362 | 0 | value = mqtt_field_value(pkt, type, fieldname_id); |
4363 | 0 | if (!istlen(value)) { |
4364 | 0 | if (isttest(value)) { |
4365 | | /* value != IST_NULL, need more data */ |
4366 | 0 | smp->flags |= SMP_F_MAY_CHANGE; |
4367 | 0 | } |
4368 | 0 | return 0; |
4369 | 0 | } |
4370 | | |
4371 | 0 | smp->data.u.str = ist2buf(value); |
4372 | 0 | smp->flags |= SMP_F_CONST; |
4373 | 0 | return 1; |
4374 | 0 | } |
4375 | | |
4376 | | /* |
4377 | | * this function checks the "mqtt_field_value" converter configuration. |
4378 | | * It expects a known packet type name or ID and a field name, in this order |
4379 | | * |
4380 | | * Args[0] will be turned into a MQTT_CPT_* value for direct matching when parsing |
4381 | | * a packet. |
4382 | | */ |
4383 | | static int sample_conv_mqtt_field_value_check(struct arg *args, struct sample_conv *conv, |
4384 | | const char *file, int line, char **err) |
4385 | 0 | { |
4386 | 0 | int type, fieldname_id; |
4387 | | |
4388 | | /* check the MQTT packet type is valid */ |
4389 | 0 | type = mqtt_typeid(ist2(args[0].data.str.area, args[0].data.str.data)); |
4390 | 0 | if (type == MQTT_CPT_INVALID) { |
4391 | 0 | memprintf(err, "Unknown MQTT type '%s'", args[0].data.str.area); |
4392 | 0 | return 0; |
4393 | 0 | } |
4394 | | |
4395 | | /* check the field name belongs to the MQTT packet type */ |
4396 | 0 | fieldname_id = mqtt_check_type_fieldname(type, ist2(args[1].data.str.area, args[1].data.str.data)); |
4397 | 0 | if (fieldname_id == MQTT_FN_INVALID) { |
4398 | 0 | memprintf(err, "Unknown MQTT field name '%s' for packet type '%s'", args[1].data.str.area, |
4399 | 0 | args[0].data.str.area); |
4400 | 0 | return 0; |
4401 | 0 | } |
4402 | | |
4403 | | /* save numeric counterparts of type and field name */ |
4404 | 0 | chunk_destroy(&args[0].data.str); |
4405 | 0 | chunk_destroy(&args[1].data.str); |
4406 | 0 | args[0].type = ARGT_SINT; |
4407 | 0 | args[0].data.sint = type; |
4408 | 0 | args[1].type = ARGT_SINT; |
4409 | 0 | args[1].data.sint = fieldname_id; |
4410 | |
|
4411 | 0 | return 1; |
4412 | 0 | } |
4413 | | |
4414 | | /* |
4415 | | * Checks that <smp> contains a valid MQTT message |
4416 | | * |
4417 | | * The function returns 1 if the check was run to its end, 0 otherwise. |
4418 | | * The result of the analyse itself is stored in <smp> as a boolean. |
4419 | | */ |
4420 | | static int sample_conv_mqtt_is_valid(const struct arg *arg_p, struct sample *smp, void *private) |
4421 | 0 | { |
4422 | 0 | struct ist msg; |
4423 | |
|
4424 | 0 | msg = ist2(smp->data.u.str.area, smp->data.u.str.data); |
4425 | |
|
4426 | 0 | smp->flags &= ~SMP_F_MAY_CHANGE; |
4427 | 0 | switch (mqtt_validate_message(msg, NULL)) { |
4428 | 0 | case FIX_VALID_MESSAGE: |
4429 | 0 | smp->data.type = SMP_T_BOOL; |
4430 | 0 | smp->data.u.sint = 1; |
4431 | 0 | return 1; |
4432 | 0 | case FIX_NEED_MORE_DATA: |
4433 | 0 | smp->flags |= SMP_F_MAY_CHANGE; |
4434 | 0 | return 0; |
4435 | 0 | case FIX_INVALID_MESSAGE: |
4436 | 0 | smp->data.type = SMP_T_BOOL; |
4437 | 0 | smp->data.u.sint = 0; |
4438 | 0 | return 1; |
4439 | 0 | } |
4440 | 0 | return 0; |
4441 | 0 | } |
4442 | | |
4443 | | /* This function checks the "strcmp" converter's arguments and extracts the |
4444 | | * variable name and its scope. |
4445 | | */ |
4446 | | static int smp_check_strcmp(struct arg *args, struct sample_conv *conv, |
4447 | | const char *file, int line, char **err) |
4448 | 0 | { |
4449 | 0 | if (!args[0].data.str.data) { |
4450 | 0 | memprintf(err, "missing variable name"); |
4451 | 0 | return 0; |
4452 | 0 | } |
4453 | | |
4454 | | /* Try to decode a variable. */ |
4455 | 0 | if (vars_check_arg(&args[0], NULL)) |
4456 | 0 | return 1; |
4457 | | |
4458 | 0 | memprintf(err, "failed to register variable name '%s'", |
4459 | 0 | args[0].data.str.area); |
4460 | 0 | return 0; |
4461 | 0 | } |
4462 | | |
4463 | | /**/ |
4464 | | static int sample_conv_htonl(const struct arg *arg_p, struct sample *smp, void *private) |
4465 | 0 | { |
4466 | 0 | struct buffer *tmp; |
4467 | 0 | uint32_t n; |
4468 | |
|
4469 | 0 | n = htonl((uint32_t)smp->data.u.sint); |
4470 | 0 | tmp = get_trash_chunk(); |
4471 | |
|
4472 | 0 | memcpy(b_head(tmp), &n, 4); |
4473 | 0 | b_add(tmp, 4); |
4474 | |
|
4475 | 0 | smp->data.u.str = *tmp; |
4476 | 0 | smp->data.type = SMP_T_BIN; |
4477 | 0 | return 1; |
4478 | 0 | } |
4479 | | |
4480 | | /**/ |
4481 | | static int sample_conv_cut_crlf(const struct arg *arg_p, struct sample *smp, void *private) |
4482 | 0 | { |
4483 | 0 | char *p; |
4484 | 0 | size_t l; |
4485 | |
|
4486 | 0 | p = smp->data.u.str.area; |
4487 | 0 | for (l = 0; l < smp->data.u.str.data; l++) { |
4488 | 0 | if (*(p+l) == '\r' || *(p+l) == '\n') |
4489 | 0 | break; |
4490 | 0 | } |
4491 | 0 | smp->data.u.str.data = l; |
4492 | 0 | return 1; |
4493 | 0 | } |
4494 | | |
4495 | | /**/ |
4496 | | static int sample_conv_ltrim(const struct arg *arg_p, struct sample *smp, void *private) |
4497 | 0 | { |
4498 | 0 | char *delimiters, *p; |
4499 | 0 | size_t dlen, l; |
4500 | |
|
4501 | 0 | delimiters = arg_p[0].data.str.area; |
4502 | 0 | dlen = arg_p[0].data.str.data; |
4503 | |
|
4504 | 0 | l = smp->data.u.str.data; |
4505 | 0 | p = smp->data.u.str.area; |
4506 | 0 | while (l && memchr(delimiters, *p, dlen) != NULL) { |
4507 | 0 | p++; |
4508 | 0 | l--; |
4509 | 0 | } |
4510 | |
|
4511 | 0 | smp->data.u.str.area = p; |
4512 | 0 | smp->data.u.str.data = l; |
4513 | 0 | return 1; |
4514 | 0 | } |
4515 | | |
4516 | | /**/ |
4517 | | static int sample_conv_rtrim(const struct arg *arg_p, struct sample *smp, void *private) |
4518 | 0 | { |
4519 | 0 | char *delimiters, *p; |
4520 | 0 | size_t dlen, l; |
4521 | |
|
4522 | 0 | delimiters = arg_p[0].data.str.area; |
4523 | 0 | dlen = arg_p[0].data.str.data; |
4524 | |
|
4525 | 0 | l = smp->data.u.str.data; |
4526 | 0 | p = smp->data.u.str.area + l - 1; |
4527 | 0 | while (l && memchr(delimiters, *p, dlen) != NULL) { |
4528 | 0 | p--; |
4529 | 0 | l--; |
4530 | 0 | } |
4531 | |
|
4532 | 0 | smp->data.u.str.data = l; |
4533 | 0 | return 1; |
4534 | 0 | } |
4535 | | |
4536 | | /* This function checks the "json_query" converter's arguments. */ |
4537 | | static int sample_check_json_query(struct arg *arg, struct sample_conv *conv, |
4538 | | const char *file, int line, char **err) |
4539 | 0 | { |
4540 | 0 | if (arg[0].data.str.data == 0) { |
4541 | 0 | memprintf(err, "json_path must not be empty"); |
4542 | 0 | return 0; |
4543 | 0 | } |
4544 | | |
4545 | 0 | if (arg[1].data.str.data != 0) { |
4546 | 0 | if (strcmp(arg[1].data.str.area, "int") != 0) { |
4547 | 0 | memprintf(err, "output_type only supports \"int\" as argument"); |
4548 | 0 | return 0; |
4549 | 0 | } else { |
4550 | 0 | arg[1].type = ARGT_SINT; |
4551 | 0 | arg[1].data.sint = 0; |
4552 | 0 | } |
4553 | 0 | } |
4554 | 0 | return 1; |
4555 | 0 | } |
4556 | | |
4557 | | /* Limit JSON integer values to the range [-(2**53)+1, (2**53)-1] as per |
4558 | | * the recommendation for interoperable integers in section 6 of RFC 7159. |
4559 | | */ |
4560 | 0 | #define JSON_INT_MAX ((1LL << 53) - 1) |
4561 | 0 | #define JSON_INT_MIN (-JSON_INT_MAX) |
4562 | | |
4563 | | /* This sample function get the value from a given json string. |
4564 | | * The mjson library is used to parse the JSON struct |
4565 | | */ |
4566 | | static int sample_conv_json_query(const struct arg *args, struct sample *smp, void *private) |
4567 | 0 | { |
4568 | 0 | const char *token; /* holds the temporary string from mjson_find */ |
4569 | 0 | int token_size; /* holds the length of <token> */ |
4570 | |
|
4571 | 0 | enum mjson_tok token_type; |
4572 | |
|
4573 | 0 | token_type = mjson_find(smp->data.u.str.area, smp->data.u.str.data, args[0].data.str.area, &token, &token_size); |
4574 | |
|
4575 | 0 | switch (token_type) { |
4576 | 0 | case MJSON_TOK_NUMBER: |
4577 | 0 | if (args[1].type == ARGT_SINT) { |
4578 | 0 | smp->data.u.sint = strtoll(token, NULL, 0); |
4579 | |
|
4580 | 0 | if (smp->data.u.sint < JSON_INT_MIN || smp->data.u.sint > JSON_INT_MAX) |
4581 | 0 | return 0; |
4582 | | |
4583 | 0 | smp->data.type = SMP_T_SINT; |
4584 | |
|
4585 | 0 | return 1; |
4586 | 0 | } else { |
4587 | 0 | struct buffer *trash = get_trash_chunk(); |
4588 | 0 | double double_val; |
4589 | |
|
4590 | 0 | if (mjson_get_number(smp->data.u.str.area, smp->data.u.str.data, args[0].data.str.area, &double_val) == 0) |
4591 | 0 | return 0; |
4592 | | |
4593 | 0 | trash->data = snprintf(trash->area,trash->size,"%g",double_val); |
4594 | 0 | smp->data.u.str = *trash; |
4595 | 0 | smp->data.type = SMP_T_STR; |
4596 | |
|
4597 | 0 | return 1; |
4598 | 0 | } |
4599 | 0 | case MJSON_TOK_TRUE: |
4600 | 0 | smp->data.type = SMP_T_BOOL; |
4601 | 0 | smp->data.u.sint = 1; |
4602 | |
|
4603 | 0 | return 1; |
4604 | 0 | case MJSON_TOK_FALSE: |
4605 | 0 | smp->data.type = SMP_T_BOOL; |
4606 | 0 | smp->data.u.sint = 0; |
4607 | |
|
4608 | 0 | return 1; |
4609 | 0 | case MJSON_TOK_STRING: { |
4610 | 0 | struct buffer *trash = get_trash_chunk_sz(token_size); |
4611 | 0 | int len; |
4612 | |
|
4613 | 0 | if (!trash) |
4614 | 0 | return 0; |
4615 | 0 | len = mjson_get_string(smp->data.u.str.area, smp->data.u.str.data, args[0].data.str.area, trash->area, trash->size); |
4616 | |
|
4617 | 0 | if (len == -1) { |
4618 | | /* invalid string */ |
4619 | 0 | return 0; |
4620 | 0 | } |
4621 | | |
4622 | 0 | trash->data = len; |
4623 | 0 | smp->data.u.str = *trash; |
4624 | 0 | smp->data.type = SMP_T_STR; |
4625 | |
|
4626 | 0 | return 1; |
4627 | 0 | } |
4628 | 0 | case MJSON_TOK_ARRAY: { |
4629 | 0 | struct buffer *trash = get_trash_chunk_sz(token_size); |
4630 | |
|
4631 | 0 | if (!trash) |
4632 | 0 | return 0; |
4633 | | // We copy the complete array, including square brackets into the return buffer |
4634 | | // result looks like: ["manage-account","manage-account-links","view-profile"] |
4635 | 0 | trash->data = b_putblk(trash, token, token_size); |
4636 | 0 | smp->data.u.str = *trash; |
4637 | 0 | smp->data.type = SMP_T_STR; |
4638 | 0 | return 1; |
4639 | 0 | } |
4640 | 0 | case MJSON_TOK_NULL: |
4641 | 0 | case MJSON_TOK_OBJECT: |
4642 | | /* We cannot handle these. */ |
4643 | 0 | return 0; |
4644 | 0 | case MJSON_TOK_INVALID: |
4645 | | /* Nothing matches the query. */ |
4646 | 0 | return 0; |
4647 | 0 | case MJSON_TOK_KEY: |
4648 | | /* This is not a valid return value according to the |
4649 | | * mjson documentation, but we handle it to benefit |
4650 | | * from '-Wswitch'. |
4651 | | */ |
4652 | 0 | return 0; |
4653 | 0 | } |
4654 | | |
4655 | 0 | my_unreachable(); |
4656 | 0 | return 0; |
4657 | 0 | } |
4658 | | |
4659 | | #ifdef USE_OPENSSL |
4660 | | static int sample_conv_jwt_verify_check(struct arg *args, struct sample_conv *conv, |
4661 | | const char *file, int line, char **err) |
4662 | | { |
4663 | | enum jwt_alg alg = JWT_ALG_DEFAULT; |
4664 | | int retval = 0; |
4665 | | |
4666 | | vars_check_arg(&args[0], NULL); |
4667 | | vars_check_arg(&args[1], NULL); |
4668 | | |
4669 | | if (args[0].type == ARGT_STR) { |
4670 | | alg = jwt_parse_alg(args[0].data.str.area, args[0].data.str.data); |
4671 | | |
4672 | | if (alg == JWT_ALG_DEFAULT) { |
4673 | | memprintf(err, "unknown JWT algorithm: %s", args[0].data.str.area); |
4674 | | return 0; |
4675 | | } |
4676 | | } |
4677 | | |
4678 | | if (args[1].type == ARGT_STR) { |
4679 | | switch (alg) { |
4680 | | case JWS_ALG_HS256: |
4681 | | case JWS_ALG_HS384: |
4682 | | case JWS_ALG_HS512: |
4683 | | /* don't try to load a file with HMAC algorithms */ |
4684 | | retval = 1; |
4685 | | break; |
4686 | | default: |
4687 | | retval = (jwt_tree_load_cert(args[1].data.str.area, args[1].data.str.data, |
4688 | | 0, file, line, err) == 0); |
4689 | | /* The second arg might be an HMAC secret but |
4690 | | * the 'alg' is stored in a var */ |
4691 | | if (!retval && args[0].type == ARGT_VAR) |
4692 | | retval = 1; |
4693 | | break; |
4694 | | } |
4695 | | } else if (args[1].type == ARGT_VAR) { |
4696 | | /* We will try to resolve the var during runtime because the |
4697 | | * processing might work if it actually points to an already |
4698 | | * existing ckch_store. |
4699 | | */ |
4700 | | retval = 1; |
4701 | | } |
4702 | | |
4703 | | return retval; |
4704 | | } |
4705 | | |
4706 | | static int sample_conv_jwt_verify_cert_check(struct arg *args, struct sample_conv *conv, |
4707 | | const char *file, int line, char **err) |
4708 | | { |
4709 | | enum jwt_alg alg = JWT_ALG_DEFAULT; |
4710 | | int retval = 0; |
4711 | | |
4712 | | vars_check_arg(&args[0], NULL); |
4713 | | vars_check_arg(&args[1], NULL); |
4714 | | |
4715 | | if (args[0].type == ARGT_STR) { |
4716 | | alg = jwt_parse_alg(args[0].data.str.area, args[0].data.str.data); |
4717 | | |
4718 | | if (alg == JWT_ALG_DEFAULT) { |
4719 | | memprintf(err, "unknown JWT algorithm: %s", args[0].data.str.area); |
4720 | | return 0; |
4721 | | } |
4722 | | } |
4723 | | |
4724 | | if (args[1].type == ARGT_STR) { |
4725 | | switch (alg) { |
4726 | | case JWS_ALG_HS256: |
4727 | | case JWS_ALG_HS384: |
4728 | | case JWS_ALG_HS512: |
4729 | | /* We can't have a certificate as second parameter for |
4730 | | * HMAC-signed JWT tokens */ |
4731 | | memprintf(err, "HMAC-signed tokens can't be processed by this converter"); |
4732 | | break; |
4733 | | default: |
4734 | | retval = (jwt_tree_load_cert(args[1].data.str.area, args[1].data.str.data, |
4735 | | 1, file, line, err) == 0); |
4736 | | break; |
4737 | | } |
4738 | | } else if (args[1].type == ARGT_VAR) { |
4739 | | /* We will try to resolve the var during runtime because the |
4740 | | * processing might work if it actually points to an already |
4741 | | * existing ckch_store. |
4742 | | */ |
4743 | | retval = 1; |
4744 | | } |
4745 | | |
4746 | | return retval; |
4747 | | } |
4748 | | |
4749 | | /* Check that a JWT's signature is correct */ |
4750 | | static int sample_conv_jwt_verify(const struct arg *args, struct sample *smp, void *private) |
4751 | | { |
4752 | | struct sample alg_smp, key_smp; |
4753 | | enum jwt_vrfy_status ret; |
4754 | | struct buffer *input = NULL; |
4755 | | struct buffer *alg = NULL; |
4756 | | struct buffer *key = NULL; |
4757 | | int retval = 0; |
4758 | | |
4759 | | /* The two following calls to 'sample_conv_var2smp_str' will both make |
4760 | | * use of the preallocated trash buffer (via get_trash_chunk call in |
4761 | | * smp_dup) which would end up erasing the contents of the 'smp' input |
4762 | | * buffer. |
4763 | | */ |
4764 | | input = alloc_trash_chunk_sz(smp->data.u.str.data); |
4765 | | if (!input) |
4766 | | return 0; |
4767 | | alg = alloc_trash_chunk(); |
4768 | | if (!alg) |
4769 | | goto end; |
4770 | | key = alloc_trash_chunk(); |
4771 | | if (!key) |
4772 | | goto end; |
4773 | | |
4774 | | if (!chunk_cpy(input, &smp->data.u.str)) |
4775 | | goto end; |
4776 | | |
4777 | | smp_set_owner(&alg_smp, smp->px, smp->sess, smp->strm, smp->opt); |
4778 | | if (!sample_conv_var2smp_str(&args[0], &alg_smp)) |
4779 | | goto end; |
4780 | | if (chunk_printf(alg, "%.*s", (int)b_data(&alg_smp.data.u.str), b_orig(&alg_smp.data.u.str)) <= 0) |
4781 | | goto end; |
4782 | | |
4783 | | smp_set_owner(&key_smp, smp->px, smp->sess, smp->strm, smp->opt); |
4784 | | if (!sample_conv_var2smp_str(&args[1], &key_smp)) |
4785 | | goto end; |
4786 | | if (chunk_printf(key, "%.*s", (int)b_data(&key_smp.data.u.str), b_orig(&key_smp.data.u.str)) <= 0) |
4787 | | goto end; |
4788 | | |
4789 | | ret = jwt_verify(input, alg, key, 0); |
4790 | | |
4791 | | smp->data.type = SMP_T_SINT; |
4792 | | smp->data.u.sint = ret; |
4793 | | |
4794 | | retval = 1; |
4795 | | |
4796 | | end: |
4797 | | free_trash_chunk(input); |
4798 | | free_trash_chunk(alg); |
4799 | | free_trash_chunk(key); |
4800 | | return retval; |
4801 | | } |
4802 | | |
4803 | | static int sample_conv_jwt_verify_cert(const struct arg *args, struct sample *smp, void *private) |
4804 | | { |
4805 | | struct sample alg_smp, key_smp; |
4806 | | enum jwt_vrfy_status ret; |
4807 | | struct buffer *input = NULL; |
4808 | | struct buffer *alg = NULL; |
4809 | | struct buffer *key = NULL; |
4810 | | int retval = 0; |
4811 | | |
4812 | | /* The two following calls to 'sample_conv_var2smp_str' will both make |
4813 | | * use of the preallocated trash buffer (via get_trash_chunk call in |
4814 | | * smp_dup) which would end up erasing the contents of the 'smp' input |
4815 | | * buffer. |
4816 | | */ |
4817 | | input = alloc_trash_chunk_sz(smp->data.u.str.data); |
4818 | | if (!input) |
4819 | | return 0; |
4820 | | alg = alloc_trash_chunk(); |
4821 | | if (!alg) |
4822 | | goto end; |
4823 | | key = alloc_trash_chunk(); |
4824 | | if (!key) |
4825 | | goto end; |
4826 | | |
4827 | | if (!chunk_cpy(input, &smp->data.u.str)) |
4828 | | goto end; |
4829 | | |
4830 | | smp_set_owner(&alg_smp, smp->px, smp->sess, smp->strm, smp->opt); |
4831 | | if (!sample_conv_var2smp_str(&args[0], &alg_smp)) |
4832 | | goto end; |
4833 | | if (chunk_printf(alg, "%.*s", (int)b_data(&alg_smp.data.u.str), b_orig(&alg_smp.data.u.str)) <= 0) |
4834 | | goto end; |
4835 | | |
4836 | | smp_set_owner(&key_smp, smp->px, smp->sess, smp->strm, smp->opt); |
4837 | | if (!sample_conv_var2smp_str(&args[1], &key_smp)) |
4838 | | goto end; |
4839 | | if (chunk_printf(key, "%.*s", (int)b_data(&key_smp.data.u.str), b_orig(&key_smp.data.u.str)) <= 0) |
4840 | | goto end; |
4841 | | |
4842 | | ret = jwt_verify(input, alg, key, 1); |
4843 | | |
4844 | | smp->data.type = SMP_T_SINT; |
4845 | | smp->data.u.sint = ret; |
4846 | | |
4847 | | retval = 1; |
4848 | | |
4849 | | end: |
4850 | | free_trash_chunk(input); |
4851 | | free_trash_chunk(alg); |
4852 | | free_trash_chunk(key); |
4853 | | return retval; |
4854 | | } |
4855 | | |
4856 | | |
4857 | | /* |
4858 | | * Returns the decoded header or payload of a JWT if no parameter is given, or |
4859 | | * the value of the specified field of the corresponding JWT subpart if a |
4860 | | * parameter is given. |
4861 | | */ |
4862 | | static int sample_conv_jwt_member_query(const struct arg *args, struct sample *smp, |
4863 | | void *private, enum jwt_elt member) |
4864 | | { |
4865 | | struct jwt_item items[JWT_ELT_MAX] = { { 0 } }; |
4866 | | unsigned int item_num = member + 1; /* We don't need to tokenize the full token */ |
4867 | | struct buffer *decoded_header; |
4868 | | int retval = 0; |
4869 | | int ret; |
4870 | | |
4871 | | /* We don't need to extract all the parts from the token, we only need a |
4872 | | * specific one. |
4873 | | */ |
4874 | | if (jwt_tokenize(&smp->data.u.str, items, item_num) < 0) |
4875 | | goto end; |
4876 | | |
4877 | | decoded_header = get_trash_chunk_sz(items[member].length); |
4878 | | if (!decoded_header) |
4879 | | goto end; |
4880 | | |
4881 | | ret = base64urldec(items[member].start, items[member].length, |
4882 | | decoded_header->area, decoded_header->size); |
4883 | | if (ret == -1) |
4884 | | goto end; |
4885 | | |
4886 | | decoded_header->data = ret; |
4887 | | if (args[0].type != ARGT_STR) { |
4888 | | smp->data.u.str = *decoded_header; |
4889 | | smp->data.type = SMP_T_STR; |
4890 | | goto end; |
4891 | | } |
4892 | | |
4893 | | /* We look for a specific field of the header or payload part of the JWT */ |
4894 | | smp->data.u.str = *decoded_header; |
4895 | | |
4896 | | retval = sample_conv_json_query(args, smp, private); |
4897 | | |
4898 | | end: |
4899 | | return retval; |
4900 | | } |
4901 | | |
4902 | | /* This function checks the "jwt_header_query" and "jwt_payload_query" converters' arguments. |
4903 | | * It is based on the "json_query" converter's check with the only difference |
4904 | | * being that the jwt converters can take 0 parameters as well. |
4905 | | */ |
4906 | | static int sample_conv_jwt_query_check(struct arg *arg, struct sample_conv *conv, |
4907 | | const char *file, int line, char **err) |
4908 | | { |
4909 | | if (arg[1].data.str.data != 0) { |
4910 | | if (strcmp(arg[1].data.str.area, "int") != 0) { |
4911 | | memprintf(err, "output_type only supports \"int\" as argument"); |
4912 | | return 0; |
4913 | | } else { |
4914 | | arg[1].type = ARGT_SINT; |
4915 | | arg[1].data.sint = 0; |
4916 | | } |
4917 | | } |
4918 | | return 1; |
4919 | | } |
4920 | | |
4921 | | /* |
4922 | | * If no parameter is given, return the decoded header part of a JWT (the first |
4923 | | * base64 encoded part, corresponding to the JOSE header). |
4924 | | * If a parameter is given, this converter acts as a "json_query" on this |
4925 | | * decoded JSON. |
4926 | | */ |
4927 | | static int sample_conv_jwt_header_query(const struct arg *args, struct sample *smp, void *private) |
4928 | | { |
4929 | | return sample_conv_jwt_member_query(args, smp, private, JWT_ELT_JOSE); |
4930 | | } |
4931 | | |
4932 | | /* |
4933 | | * If no parameter is given, return the decoded payload part of a JWT (the |
4934 | | * second base64 encoded part, which contains all the claims). If a parameter |
4935 | | * is given, this converter acts as a "json_query" on this decoded JSON. |
4936 | | */ |
4937 | | static int sample_conv_jwt_payload_query(const struct arg *args, struct sample *smp, void *private) |
4938 | | { |
4939 | | return sample_conv_jwt_member_query(args, smp, private, JWT_ELT_CLAIMS); |
4940 | | } |
4941 | | |
4942 | | #endif /* USE_OPENSSL */ |
4943 | | |
4944 | | /************************************************************************/ |
4945 | | /* All supported sample fetch functions must be declared here */ |
4946 | | /************************************************************************/ |
4947 | | |
4948 | | |
4949 | | /* returns the actconn */ |
4950 | | static int |
4951 | | smp_fetch_actconn(const struct arg *args, struct sample *smp, const char *kw, void *private) |
4952 | 0 | { |
4953 | 0 | smp->data.type = SMP_T_SINT; |
4954 | 0 | smp->data.u.sint = actconn; |
4955 | 0 | return 1; |
4956 | 0 | } |
4957 | | |
4958 | | |
4959 | | /* force TRUE to be returned at the fetch level */ |
4960 | | static int |
4961 | | smp_fetch_true(const struct arg *args, struct sample *smp, const char *kw, void *private) |
4962 | 0 | { |
4963 | 0 | if (!smp_make_rw(smp)) |
4964 | 0 | return 0; |
4965 | | |
4966 | 0 | smp->data.type = SMP_T_BOOL; |
4967 | 0 | smp->data.u.sint = 1; |
4968 | 0 | return 1; |
4969 | 0 | } |
4970 | | |
4971 | | /* force FALSE to be returned at the fetch level */ |
4972 | | static int |
4973 | | smp_fetch_false(const struct arg *args, struct sample *smp, const char *kw, void *private) |
4974 | 0 | { |
4975 | 0 | smp->data.type = SMP_T_BOOL; |
4976 | 0 | smp->data.u.sint = 0; |
4977 | 0 | return 1; |
4978 | 0 | } |
4979 | | |
4980 | | /* retrieve environment variable $1 as a string */ |
4981 | | static int |
4982 | | smp_fetch_env(const struct arg *args, struct sample *smp, const char *kw, void *private) |
4983 | 0 | { |
4984 | 0 | char *env; |
4985 | |
|
4986 | 0 | if (args[0].type != ARGT_STR) |
4987 | 0 | return 0; |
4988 | | |
4989 | 0 | env = getenv(args[0].data.str.area); |
4990 | 0 | if (!env) |
4991 | 0 | return 0; |
4992 | | |
4993 | 0 | smp->data.type = SMP_T_STR; |
4994 | 0 | smp->flags = SMP_F_CONST; |
4995 | 0 | smp->data.u.str.area = env; |
4996 | 0 | smp->data.u.str.data = strlen(env); |
4997 | 0 | return 1; |
4998 | 0 | } |
4999 | | |
5000 | | /* Validates the data unit argument passed to "date" fetch. Argument 1 support an |
5001 | | * optional string representing the unit of the result: "s" for seconds, "ms" for |
5002 | | * milliseconds and "us" for microseconds. |
5003 | | * Returns 0 on error and non-zero if OK. |
5004 | | */ |
5005 | | int smp_check_date_unit(struct arg *args, char **err) |
5006 | 0 | { |
5007 | 0 | if (args[1].type == ARGT_STR) { |
5008 | 0 | long long int unit; |
5009 | |
|
5010 | 0 | if (strcmp(args[1].data.str.area, "s") == 0) { |
5011 | 0 | unit = TIME_UNIT_S; |
5012 | 0 | } |
5013 | 0 | else if (strcmp(args[1].data.str.area, "ms") == 0) { |
5014 | 0 | unit = TIME_UNIT_MS; |
5015 | 0 | } |
5016 | 0 | else if (strcmp(args[1].data.str.area, "us") == 0) { |
5017 | 0 | unit = TIME_UNIT_US; |
5018 | 0 | } |
5019 | 0 | else { |
5020 | 0 | memprintf(err, "expects 's', 'ms' or 'us', got '%s'", |
5021 | 0 | args[1].data.str.area); |
5022 | 0 | return 0; |
5023 | 0 | } |
5024 | | |
5025 | 0 | chunk_destroy(&args[1].data.str); |
5026 | 0 | args[1].type = ARGT_SINT; |
5027 | 0 | args[1].data.sint = unit; |
5028 | 0 | } |
5029 | 0 | else if (args[1].type != ARGT_STOP) { |
5030 | 0 | memprintf(err, "Unexpected arg type"); |
5031 | 0 | return 0; |
5032 | 0 | } |
5033 | | |
5034 | 0 | return 1; |
5035 | 0 | } |
5036 | | |
5037 | | /* retrieve the current local date in epoch time, converts it to milliseconds |
5038 | | * or microseconds if asked to in optional args[1] unit param, and applies an |
5039 | | * optional args[0] offset. |
5040 | | */ |
5041 | | static int |
5042 | | smp_fetch_date(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5043 | 0 | { |
5044 | 0 | smp->data.u.sint = date.tv_sec; |
5045 | | |
5046 | | /* report in milliseconds */ |
5047 | 0 | if (args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_MS) { |
5048 | 0 | smp->data.u.sint *= 1000; |
5049 | 0 | smp->data.u.sint += date.tv_usec / 1000; |
5050 | 0 | } |
5051 | | /* report in microseconds */ |
5052 | 0 | else if (args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_US) { |
5053 | 0 | smp->data.u.sint *= 1000000; |
5054 | 0 | smp->data.u.sint += date.tv_usec; |
5055 | 0 | } |
5056 | | |
5057 | | /* add offset */ |
5058 | 0 | if (args[0].type == ARGT_SINT) |
5059 | 0 | smp->data.u.sint += args[0].data.sint; |
5060 | |
|
5061 | 0 | smp->data.type = SMP_T_SINT; |
5062 | 0 | smp->flags |= SMP_F_VOL_TEST | SMP_F_MAY_CHANGE; |
5063 | 0 | return 1; |
5064 | 0 | } |
5065 | | |
5066 | | /* retrieve the current microsecond part of the date */ |
5067 | | static int |
5068 | | smp_fetch_date_us(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5069 | 0 | { |
5070 | 0 | smp->data.u.sint = date.tv_usec; |
5071 | 0 | smp->data.type = SMP_T_SINT; |
5072 | 0 | smp->flags |= SMP_F_VOL_TEST | SMP_F_MAY_CHANGE; |
5073 | 0 | return 1; |
5074 | 0 | } |
5075 | | |
5076 | | |
5077 | | /* returns the hostname */ |
5078 | | static int |
5079 | | smp_fetch_hostname(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5080 | 0 | { |
5081 | 0 | smp->data.type = SMP_T_STR; |
5082 | 0 | smp->flags = SMP_F_CONST; |
5083 | 0 | smp->data.u.str.area = hostname; |
5084 | 0 | smp->data.u.str.data = strlen(hostname); |
5085 | 0 | return 1; |
5086 | 0 | } |
5087 | | |
5088 | | /* returns the number of processes */ |
5089 | | static int |
5090 | | smp_fetch_nbproc(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5091 | 0 | { |
5092 | 0 | smp->data.type = SMP_T_SINT; |
5093 | 0 | smp->data.u.sint = 1; |
5094 | 0 | return 1; |
5095 | 0 | } |
5096 | | |
5097 | | /* returns the PID of the current process */ |
5098 | | static int |
5099 | | smp_fetch_pid(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5100 | 0 | { |
5101 | 0 | smp->data.type = SMP_T_SINT; |
5102 | 0 | smp->data.u.sint = pid; |
5103 | 0 | return 1; |
5104 | 0 | } |
5105 | | |
5106 | | |
5107 | | /* returns the number of the current process (between 1 and nbproc) */ |
5108 | | static int |
5109 | | smp_fetch_proc(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5110 | 0 | { |
5111 | 0 | smp->data.type = SMP_T_SINT; |
5112 | 0 | smp->data.u.sint = 1; |
5113 | 0 | return 1; |
5114 | 0 | } |
5115 | | |
5116 | | /* returns the number of the current thread (between 0 and nbthread-1) */ |
5117 | | static int |
5118 | | smp_fetch_thread(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5119 | 0 | { |
5120 | 0 | smp->data.type = SMP_T_SINT; |
5121 | 0 | smp->data.u.sint = tid; |
5122 | 0 | return 1; |
5123 | 0 | } |
5124 | | |
5125 | | /* returns the number of the current thread group (between 0 and nbtgroups-1) */ |
5126 | | static int |
5127 | | smp_fetch_tgroup(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5128 | 0 | { |
5129 | 0 | smp->data.type = SMP_T_SINT; |
5130 | 0 | smp->data.u.sint = tgid - 1; // tgid starts at 1 |
5131 | 0 | return 1; |
5132 | 0 | } |
5133 | | |
5134 | | /* returns the last known CPU usage of the current thread */ |
5135 | | static int |
5136 | | smp_fetch_cpu_usage_thr(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5137 | 0 | { |
5138 | 0 | smp->data.type = SMP_T_SINT; |
5139 | 0 | smp->data.u.sint = 100 - th_ctx->idle_pct; |
5140 | 0 | return 1; |
5141 | 0 | } |
5142 | | |
5143 | | /* returns the last known CPU usage of the current thread group */ |
5144 | | static int |
5145 | | smp_fetch_cpu_usage_grp(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5146 | 0 | { |
5147 | 0 | uint thr, tot = 0; |
5148 | |
|
5149 | 0 | for (thr = 0; thr < ha_tgroup_info[tgid - 1].count; thr++) |
5150 | 0 | tot += 100 - ha_thread_ctx[ha_tgroup_info[tgid - 1].base + thr].idle_pct; |
5151 | |
|
5152 | 0 | smp->data.type = SMP_T_SINT; |
5153 | 0 | smp->data.u.sint = (tot + thr / 2) / thr; |
5154 | 0 | return 1; |
5155 | 0 | } |
5156 | | |
5157 | | /* returns the last known CPU usage of the whole process */ |
5158 | | static int |
5159 | | smp_fetch_cpu_usage_proc(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5160 | 0 | { |
5161 | 0 | int thr, tot = 0; |
5162 | |
|
5163 | 0 | for (thr = 0; thr < global.nbthread; thr++) |
5164 | 0 | tot += 100 - ha_thread_ctx[thr].idle_pct; |
5165 | |
|
5166 | 0 | smp->data.type = SMP_T_SINT; |
5167 | 0 | smp->data.u.sint = (tot + thr / 2) / thr; |
5168 | 0 | return 1; |
5169 | 0 | } |
5170 | | |
5171 | | /* generate a random 32-bit integer for whatever purpose, with an optional |
5172 | | * range specified in argument. |
5173 | | */ |
5174 | | static int |
5175 | | smp_fetch_rand(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5176 | 0 | { |
5177 | 0 | smp->data.u.sint = statistical_prng(); |
5178 | | |
5179 | | /* reduce if needed. Don't do a modulo, use all bits! */ |
5180 | 0 | if (args[0].type == ARGT_SINT) |
5181 | 0 | smp->data.u.sint = ((u64)smp->data.u.sint * (u64)args[0].data.sint) >> 32; |
5182 | |
|
5183 | 0 | smp->data.type = SMP_T_SINT; |
5184 | 0 | smp->flags |= SMP_F_VOL_TEST | SMP_F_MAY_CHANGE; |
5185 | 0 | return 1; |
5186 | 0 | } |
5187 | | |
5188 | | /* returns true if the current process is stopping */ |
5189 | | static int |
5190 | | smp_fetch_stopping(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5191 | 0 | { |
5192 | 0 | smp->data.type = SMP_T_BOOL; |
5193 | 0 | smp->data.u.sint = stopping; |
5194 | 0 | return 1; |
5195 | 0 | } |
5196 | | |
5197 | | /* returns the number of calls of the current stream's process_stream() */ |
5198 | | static int |
5199 | | smp_fetch_cpu_calls(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5200 | 0 | { |
5201 | 0 | if (!smp->strm) |
5202 | 0 | return 0; |
5203 | | |
5204 | 0 | smp->data.type = SMP_T_SINT; |
5205 | 0 | smp->data.u.sint = smp->strm->task->calls; |
5206 | 0 | return 1; |
5207 | 0 | } |
5208 | | |
5209 | | /* returns the average number of nanoseconds spent processing the stream per call */ |
5210 | | static int |
5211 | | smp_fetch_cpu_ns_avg(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5212 | 0 | { |
5213 | 0 | if (!smp->strm) |
5214 | 0 | return 0; |
5215 | | |
5216 | 0 | smp->data.type = SMP_T_SINT; |
5217 | 0 | smp->data.u.sint = smp->strm->task->calls ? smp->strm->cpu_time / smp->strm->task->calls : 0; |
5218 | 0 | return 1; |
5219 | 0 | } |
5220 | | |
5221 | | /* returns the total number of nanoseconds spent processing the stream */ |
5222 | | static int |
5223 | | smp_fetch_cpu_ns_tot(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5224 | 0 | { |
5225 | 0 | if (!smp->strm) |
5226 | 0 | return 0; |
5227 | | |
5228 | 0 | smp->data.type = SMP_T_SINT; |
5229 | 0 | smp->data.u.sint = smp->strm->cpu_time; |
5230 | 0 | return 1; |
5231 | 0 | } |
5232 | | |
5233 | | /* returns the average number of nanoseconds per call spent waiting for other tasks to be processed */ |
5234 | | static int |
5235 | | smp_fetch_lat_ns_avg(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5236 | 0 | { |
5237 | 0 | if (!smp->strm) |
5238 | 0 | return 0; |
5239 | | |
5240 | 0 | smp->data.type = SMP_T_SINT; |
5241 | 0 | smp->data.u.sint = smp->strm->task->calls ? smp->strm->lat_time / smp->strm->task->calls : 0; |
5242 | 0 | return 1; |
5243 | 0 | } |
5244 | | |
5245 | | /* returns the total number of nanoseconds per call spent waiting for other tasks to be processed */ |
5246 | | static int |
5247 | | smp_fetch_lat_ns_tot(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5248 | 0 | { |
5249 | 0 | if (!smp->strm) |
5250 | 0 | return 0; |
5251 | | |
5252 | 0 | smp->data.type = SMP_T_SINT; |
5253 | 0 | smp->data.u.sint = smp->strm->lat_time; |
5254 | 0 | return 1; |
5255 | 0 | } |
5256 | | |
5257 | | static int smp_fetch_const_str(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5258 | 0 | { |
5259 | 0 | smp->flags |= SMP_F_CONST; |
5260 | 0 | smp->data.type = SMP_T_STR; |
5261 | 0 | smp->data.u.str.area = args[0].data.str.area; |
5262 | 0 | smp->data.u.str.data = args[0].data.str.data; |
5263 | 0 | return 1; |
5264 | 0 | } |
5265 | | |
5266 | | static int smp_check_const_bool(struct arg *args, char **err) |
5267 | 0 | { |
5268 | 0 | if (strcasecmp(args[0].data.str.area, "true") == 0 || |
5269 | 0 | strcasecmp(args[0].data.str.area, "1") == 0) { |
5270 | 0 | chunk_destroy(&args[0].data.str); |
5271 | 0 | args[0].type = ARGT_SINT; |
5272 | 0 | args[0].data.sint = 1; |
5273 | 0 | return 1; |
5274 | 0 | } |
5275 | 0 | if (strcasecmp(args[0].data.str.area, "false") == 0 || |
5276 | 0 | strcasecmp(args[0].data.str.area, "0") == 0) { |
5277 | 0 | chunk_destroy(&args[0].data.str); |
5278 | 0 | args[0].type = ARGT_SINT; |
5279 | 0 | args[0].data.sint = 0; |
5280 | 0 | return 1; |
5281 | 0 | } |
5282 | 0 | memprintf(err, "Expects 'true', 'false', '0' or '1'"); |
5283 | 0 | return 0; |
5284 | 0 | } |
5285 | | |
5286 | | static int smp_fetch_const_bool(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5287 | 0 | { |
5288 | 0 | smp->data.type = SMP_T_BOOL; |
5289 | 0 | smp->data.u.sint = args[0].data.sint; |
5290 | 0 | return 1; |
5291 | 0 | } |
5292 | | |
5293 | | static int smp_fetch_const_int(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5294 | 0 | { |
5295 | 0 | smp->data.type = SMP_T_SINT; |
5296 | 0 | smp->data.u.sint = args[0].data.sint; |
5297 | 0 | return 1; |
5298 | 0 | } |
5299 | | |
5300 | | static int smp_fetch_const_ipv4(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5301 | 0 | { |
5302 | 0 | smp->data.type = SMP_T_IPV4; |
5303 | 0 | smp->data.u.ipv4 = args[0].data.ipv4; |
5304 | 0 | return 1; |
5305 | 0 | } |
5306 | | |
5307 | | static int smp_fetch_const_ipv6(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5308 | 0 | { |
5309 | 0 | smp->data.type = SMP_T_IPV6; |
5310 | 0 | smp->data.u.ipv6 = args[0].data.ipv6; |
5311 | 0 | return 1; |
5312 | 0 | } |
5313 | | |
5314 | | static int smp_check_const_bin(struct arg *args, char **err) |
5315 | 0 | { |
5316 | 0 | char *binstr = NULL; |
5317 | 0 | int binstrlen; |
5318 | |
|
5319 | 0 | if (!parse_binary(args[0].data.str.area, &binstr, &binstrlen, err)) |
5320 | 0 | return 0; |
5321 | 0 | chunk_destroy(&args[0].data.str); |
5322 | 0 | args[0].type = ARGT_STR; |
5323 | 0 | args[0].data.str.area = binstr; |
5324 | 0 | args[0].data.str.data = binstrlen; |
5325 | 0 | return 1; |
5326 | 0 | } |
5327 | | |
5328 | | static int smp_fetch_const_bin(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5329 | 0 | { |
5330 | 0 | smp->flags |= SMP_F_CONST; |
5331 | 0 | smp->data.type = SMP_T_BIN; |
5332 | 0 | smp->data.u.str.area = args[0].data.str.area; |
5333 | 0 | smp->data.u.str.data = args[0].data.str.data; |
5334 | 0 | return 1; |
5335 | 0 | } |
5336 | | |
5337 | | static int smp_check_const_meth(struct arg *args, char **err) |
5338 | 0 | { |
5339 | 0 | enum http_meth_t meth; |
5340 | 0 | int i; |
5341 | |
|
5342 | 0 | meth = find_http_meth(args[0].data.str.area, args[0].data.str.data); |
5343 | 0 | if (meth != HTTP_METH_OTHER) { |
5344 | 0 | chunk_destroy(&args[0].data.str); |
5345 | 0 | args[0].type = ARGT_SINT; |
5346 | 0 | args[0].data.sint = meth; |
5347 | 0 | } else { |
5348 | | /* Check method availability. A method is a token defined as : |
5349 | | * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / |
5350 | | * "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA |
5351 | | * token = 1*tchar |
5352 | | */ |
5353 | 0 | for (i = 0; i < args[0].data.str.data; i++) { |
5354 | 0 | if (!HTTP_IS_TOKEN(args[0].data.str.area[i])) { |
5355 | 0 | memprintf(err, "expects valid method."); |
5356 | 0 | return 0; |
5357 | 0 | } |
5358 | 0 | } |
5359 | 0 | } |
5360 | 0 | return 1; |
5361 | 0 | } |
5362 | | |
5363 | | static int smp_fetch_const_meth(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5364 | 0 | { |
5365 | 0 | smp->data.type = SMP_T_METH; |
5366 | 0 | if (args[0].type == ARGT_SINT) { |
5367 | 0 | smp->flags &= ~SMP_F_CONST; |
5368 | 0 | smp->data.u.meth.meth = args[0].data.sint; |
5369 | 0 | smp->data.u.meth.str.area = ""; |
5370 | 0 | smp->data.u.meth.str.data = 0; |
5371 | 0 | } else { |
5372 | 0 | smp->flags |= SMP_F_CONST; |
5373 | 0 | smp->data.u.meth.meth = HTTP_METH_OTHER; |
5374 | 0 | smp->data.u.meth.str.area = args[0].data.str.area; |
5375 | 0 | smp->data.u.meth.str.data = args[0].data.str.data; |
5376 | 0 | } |
5377 | 0 | return 1; |
5378 | 0 | } |
5379 | | |
5380 | | // This function checks the "uuid" sample's arguments. |
5381 | | // Function won't get called when no parameter is specified (maybe a bug?) |
5382 | | static int smp_check_uuid(struct arg *args, char **err) |
5383 | 0 | { |
5384 | 0 | if (!args[0].type) { |
5385 | 0 | args[0].type = ARGT_SINT; |
5386 | 0 | args[0].data.sint = 4; |
5387 | 0 | } else { |
5388 | 0 | switch (args[0].data.sint) { |
5389 | 0 | case 4: |
5390 | 0 | case 7: |
5391 | 0 | break; |
5392 | 0 | default: |
5393 | 0 | memprintf(err, "Unsupported UUID version: '%lld'", args[0].data.sint); |
5394 | 0 | return 0; |
5395 | 0 | } |
5396 | 0 | } |
5397 | | |
5398 | 0 | return 1; |
5399 | 0 | } |
5400 | | |
5401 | | // Generate a RFC 9562 UUID (default is v4 = fully random) |
5402 | | static int smp_fetch_uuid(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5403 | 0 | { |
5404 | 0 | long long int type = -1; |
5405 | |
|
5406 | 0 | if (!args[0].type) { |
5407 | 0 | type = 4; |
5408 | 0 | } else { |
5409 | 0 | type = args[0].data.sint; |
5410 | 0 | } |
5411 | |
|
5412 | 0 | switch (type) { |
5413 | 0 | case 4: |
5414 | 0 | ha_generate_uuid_v4(&trash); |
5415 | 0 | break; |
5416 | 0 | case 7: |
5417 | 0 | ha_generate_uuid_v7(&trash); |
5418 | 0 | break; |
5419 | 0 | default: |
5420 | 0 | return 0; |
5421 | 0 | } |
5422 | | |
5423 | 0 | smp->data.type = SMP_T_STR; |
5424 | 0 | smp->flags = SMP_F_VOL_TEST | SMP_F_MAY_CHANGE; |
5425 | 0 | smp->data.u.str = trash; |
5426 | 0 | return 1; |
5427 | 0 | } |
5428 | | |
5429 | | /* returns the uptime in seconds */ |
5430 | | static int |
5431 | | smp_fetch_uptime(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5432 | 0 | { |
5433 | 0 | smp->data.type = SMP_T_SINT; |
5434 | 0 | smp->data.u.sint = ns_to_sec(now_ns - start_time_ns); |
5435 | 0 | return 1; |
5436 | 0 | } |
5437 | | |
5438 | | |
5439 | | /* Check if QUIC support was compiled and was not disabled by "tune.quic.listen" global option */ |
5440 | | static int smp_fetch_quic_enabled(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5441 | 0 | { |
5442 | 0 | smp->data.type = SMP_T_BOOL; |
5443 | 0 | smp->flags = 0; |
5444 | | #ifdef USE_QUIC |
5445 | | smp->data.u.sint = !(quic_tune.fe.opts & QUIC_TUNE_FE_LISTEN_OFF); |
5446 | | #else |
5447 | 0 | smp->data.u.sint = 0; |
5448 | 0 | #endif |
5449 | 0 | return smp->data.u.sint; |
5450 | 0 | } |
5451 | | |
5452 | | /* Timing events re{q,s}.timer. */ |
5453 | | static int smp_fetch_reX_timers(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5454 | 0 | { |
5455 | 0 | struct strm_logs *logs; |
5456 | 0 | int t_request = -1; |
5457 | |
|
5458 | 0 | if (!smp->strm) |
5459 | 0 | return 0; |
5460 | | |
5461 | 0 | smp->data.type = SMP_T_SINT; |
5462 | 0 | smp->flags = 0; |
5463 | |
|
5464 | 0 | logs = &smp->strm->logs; |
5465 | | |
5466 | |
|
5467 | 0 | if ((llong)(logs->request_ts - logs->accept_ts) >= 0) |
5468 | 0 | t_request = ns_to_ms(logs->request_ts - logs->accept_ts); |
5469 | | |
5470 | | /* req.timer. */ |
5471 | 0 | if (kw[2] == 'q') { |
5472 | |
|
5473 | 0 | switch (kw[10]) { |
5474 | | |
5475 | | /* req.timer.idle (%Ti) */ |
5476 | 0 | case 'i': |
5477 | 0 | smp->data.u.sint = logs->t_idle; |
5478 | 0 | break; |
5479 | | |
5480 | | /* req.timer.tq (%Tq) */ |
5481 | 0 | case 't': |
5482 | 0 | smp->data.u.sint = t_request; |
5483 | 0 | break; |
5484 | | |
5485 | | /* req.timer.hdr (%TR) */ |
5486 | 0 | case 'h': |
5487 | 0 | smp->data.u.sint = (t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1; |
5488 | 0 | break; |
5489 | | |
5490 | | /* req.timer.queue (%Tw) */ |
5491 | 0 | case 'q': |
5492 | 0 | smp->data.u.sint = (logs->t_queue >= 0) ? logs->t_queue - t_request : -1; |
5493 | 0 | break; |
5494 | | |
5495 | 0 | default: |
5496 | 0 | goto error; |
5497 | |
|
5498 | 0 | } |
5499 | 0 | } else { |
5500 | | /* res.timer. */ |
5501 | 0 | switch (kw[10]) { |
5502 | | /* res.timer.hdr (%Tr) */ |
5503 | 0 | case 'h': |
5504 | 0 | smp->data.u.sint = (logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1; |
5505 | 0 | break; |
5506 | | |
5507 | | /* res.timer.data (%Td) */ |
5508 | 0 | case 'd': |
5509 | 0 | smp->data.u.sint = (logs->t_data >= 0) ? logs->t_close - logs->t_data : -1; |
5510 | 0 | break; |
5511 | | |
5512 | 0 | default: |
5513 | 0 | goto error; |
5514 | |
|
5515 | 0 | } |
5516 | |
|
5517 | 0 | } |
5518 | | |
5519 | 0 | return 1; |
5520 | 0 | error: |
5521 | |
|
5522 | 0 | return 0; |
5523 | 0 | } |
5524 | | |
5525 | | |
5526 | | /* Timing events txn. */ |
5527 | | static int smp_fetch_txn_timers(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5528 | 0 | { |
5529 | 0 | struct strm_logs *logs; |
5530 | |
|
5531 | 0 | if (!smp->strm) |
5532 | 0 | return 0; |
5533 | | |
5534 | 0 | smp->data.type = SMP_T_SINT; |
5535 | 0 | smp->flags = 0; |
5536 | |
|
5537 | 0 | logs = &smp->strm->logs; |
5538 | | |
5539 | | /* txn.timer. */ |
5540 | 0 | switch (kw[10]) { |
5541 | | |
5542 | | /* txn.timer.total (%Ta) */ |
5543 | 0 | case 't': |
5544 | 0 | smp->data.u.sint = logs->t_close - (logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0); |
5545 | 0 | break; |
5546 | | |
5547 | | |
5548 | | /* txn.timer.user (%Tu) */ |
5549 | 0 | case 'u': |
5550 | 0 | smp->data.u.sint = logs->t_close - (logs->t_idle >= 0 ? logs->t_idle : 0); |
5551 | 0 | break; |
5552 | | |
5553 | 0 | default: |
5554 | 0 | goto error; |
5555 | |
|
5556 | 0 | } |
5557 | | |
5558 | 0 | return 1; |
5559 | 0 | error: |
5560 | |
|
5561 | 0 | return 0; |
5562 | 0 | } |
5563 | | |
5564 | | /* Server conn queueing infos - bc_{be,srv}_queue */ |
5565 | | static int smp_fetch_conn_queues(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5566 | 0 | { |
5567 | 0 | struct strm_logs *logs; |
5568 | |
|
5569 | 0 | if (!smp->strm) |
5570 | 0 | return 0; |
5571 | | |
5572 | 0 | smp->data.type = SMP_T_SINT; |
5573 | 0 | smp->flags = 0; |
5574 | |
|
5575 | 0 | logs = &smp->strm->logs; |
5576 | |
|
5577 | 0 | if (kw[3] == 'b') { |
5578 | | /* bc_be_queue */ |
5579 | 0 | smp->data.u.sint = logs->prx_queue_pos; |
5580 | 0 | } |
5581 | 0 | else { |
5582 | | /* bc_srv_queue */ |
5583 | 0 | smp->data.u.sint = logs->srv_queue_pos; |
5584 | 0 | } |
5585 | 0 | return 1; |
5586 | 0 | } |
5587 | | |
5588 | | /* Timing events {f,bc}.timer. */ |
5589 | | static int smp_fetch_conn_timers(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5590 | 0 | { |
5591 | 0 | struct strm_logs *logs; |
5592 | |
|
5593 | 0 | smp->data.type = SMP_T_SINT; |
5594 | 0 | smp->flags = 0; |
5595 | |
|
5596 | 0 | if (!smp->strm) { |
5597 | | /* no stream: only fc.timer.handshake is available via |
5598 | | * the session. |
5599 | | */ |
5600 | 0 | if (kw[0] == 'f' && kw[9] == 'h') { |
5601 | 0 | smp->data.u.sint = smp->sess->t_handshake; |
5602 | 0 | return 1; |
5603 | 0 | } |
5604 | 0 | return 0; |
5605 | 0 | } |
5606 | | |
5607 | 0 | logs = &smp->strm->logs; |
5608 | |
|
5609 | 0 | if (kw[0] == 'b') { |
5610 | | /* fc.timer. */ |
5611 | 0 | switch (kw[9]) { |
5612 | | |
5613 | | /* bc.timer.connect (%Tc) */ |
5614 | 0 | case 'c': |
5615 | 0 | smp->data.u.sint = (logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1; |
5616 | 0 | break; |
5617 | | |
5618 | 0 | default: |
5619 | 0 | goto error; |
5620 | 0 | } |
5621 | |
|
5622 | 0 | } else { |
5623 | | |
5624 | | /* fc.timer. */ |
5625 | 0 | switch (kw[9]) { |
5626 | | |
5627 | | /* fc.timer.handshake (%Th) */ |
5628 | 0 | case 'h': |
5629 | 0 | smp->data.u.sint = logs->t_handshake; |
5630 | 0 | break; |
5631 | | |
5632 | | /* fc,timer.total (%Tt) */ |
5633 | 0 | case 't': |
5634 | 0 | smp->data.u.sint = logs->t_close; |
5635 | 0 | break; |
5636 | | |
5637 | 0 | default: |
5638 | 0 | goto error; |
5639 | 0 | } |
5640 | |
|
5641 | 0 | } |
5642 | | |
5643 | 0 | return 1; |
5644 | 0 | error: |
5645 | |
|
5646 | 0 | return 0; |
5647 | 0 | } |
5648 | | |
5649 | | /* bytes_{in,out} */ |
5650 | | static int smp_fetch_bytes(const struct arg *args, struct sample *smp, const char *kw, void *private) |
5651 | 0 | { |
5652 | 0 | struct strm_logs *logs; |
5653 | |
|
5654 | 0 | if (!smp->strm) |
5655 | 0 | return 0; |
5656 | | |
5657 | 0 | smp->data.type = SMP_T_SINT; |
5658 | 0 | smp->flags = 0; |
5659 | |
|
5660 | 0 | logs = &smp->strm->logs; |
5661 | 0 | if (!logs) |
5662 | 0 | return 0; |
5663 | | |
5664 | 0 | if (kw[2] == 'q') /* req.bytes_in or req.bytes_out */ |
5665 | 0 | smp->data.u.sint = (kw[10] == 'i') ? logs->req_in : logs->req_out; |
5666 | 0 | else if (kw[2] == 's') /* res.bytes_in or res.bytes_out */ |
5667 | 0 | smp->data.u.sint = (kw[10] == 'i') ? logs->res_in : logs->res_out; |
5668 | 0 | else /* bytes_in or bytes_out */ |
5669 | 0 | smp->data.u.sint = (kw[6] == 'i') ? logs->req_in : logs->res_in; |
5670 | |
|
5671 | 0 | return 1; |
5672 | 0 | } |
5673 | | |
5674 | | static int sample_conv_bytes_check(struct arg *args, struct sample_conv *conv, |
5675 | | const char *file, int line, char **err) |
5676 | 0 | { |
5677 | | // arg0 is not optional, must be >= 0 |
5678 | 0 | if (!check_operator(&args[0], conv, file, line, err)) { |
5679 | 0 | return 0; |
5680 | 0 | } |
5681 | 0 | if (args[0].type != ARGT_VAR) { |
5682 | 0 | if (args[0].type != ARGT_SINT || args[0].data.sint < 0) { |
5683 | 0 | memprintf(err, "expects a non-negative integer"); |
5684 | 0 | return 0; |
5685 | 0 | } |
5686 | 0 | } |
5687 | | // arg1 is optional, must be > 0 |
5688 | 0 | if (args[1].type != ARGT_STOP) { |
5689 | 0 | if (!check_operator(&args[1], conv, file, line, err)) { |
5690 | 0 | return 0; |
5691 | 0 | } |
5692 | 0 | if (args[1].type != ARGT_VAR) { |
5693 | 0 | if (args[1].type != ARGT_SINT || args[1].data.sint <= 0) { |
5694 | 0 | memprintf(err, "expects a positive integer"); |
5695 | 0 | return 0; |
5696 | 0 | } |
5697 | 0 | } |
5698 | 0 | } |
5699 | | |
5700 | 0 | return 1; |
5701 | 0 | } |
5702 | | |
5703 | | static struct sample_fetch_kw_list smp_logs_kws = {ILH, { |
5704 | | { "bytes_in", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_RQFIN }, |
5705 | | { "bytes_out", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_RSFIN }, |
5706 | | |
5707 | | { "txn.timer.total", smp_fetch_txn_timers, 0, NULL, SMP_T_SINT, SMP_USE_TXFIN }, /* "Ta" */ |
5708 | | { "txn.timer.user", smp_fetch_txn_timers, 0, NULL, SMP_T_SINT, SMP_USE_TXFIN }, /* "Tu" */ |
5709 | | |
5710 | | { "bc.timer.connect", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "Tc" */ |
5711 | | { "bc_be_queue", smp_fetch_conn_queues, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "bq" */ |
5712 | | { "bc_srv_queue", smp_fetch_conn_queues, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "sq" */ |
5713 | | |
5714 | | { "fc.timer.handshake", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI }, /* "Th" */ |
5715 | | { "fc.timer.total", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_SSFIN }, /* "Tt" */ |
5716 | | |
5717 | | { "req.bytes_in", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_RQFIN }, |
5718 | | { "req.bytes_out", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_RQFIN }, |
5719 | | { "req.timer.idle", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "Ti" */ |
5720 | | { "req.timer.tq", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "Tq" */ |
5721 | | { "req.timer.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "TR" */ |
5722 | | { "req.timer.queue", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "Tw" */ |
5723 | | { "res.bytes_in", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_RSFIN }, |
5724 | | { "res.bytes_out", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_RSFIN }, |
5725 | | { "res.timer.data", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_RSFIN }, /* "Td" */ |
5726 | | { "res.timer.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV }, /* "Tr" */ |
5727 | | { /* END */ }, |
5728 | | }}; |
5729 | | |
5730 | | INITCALL1(STG_REGISTER, sample_register_fetches, &smp_logs_kws); |
5731 | | |
5732 | | /* Note: must not be declared <const> as its list will be overwritten. |
5733 | | * Note: fetches that may return multiple types should be declared using the |
5734 | | * appropriate pseudo-type. If not available it must be declared as the lowest |
5735 | | * common denominator, the type that can be casted into all other ones. |
5736 | | */ |
5737 | | static struct sample_fetch_kw_list smp_kws = {ILH, { |
5738 | | { "act_conn", smp_fetch_actconn, 0, NULL, SMP_T_SINT, SMP_USE_CONST }, |
5739 | | { "always_false", smp_fetch_false, 0, NULL, SMP_T_BOOL, SMP_USE_CONST }, |
5740 | | { "always_true", smp_fetch_true, 0, NULL, SMP_T_BOOL, SMP_USE_CONST }, |
5741 | | { "env", smp_fetch_env, ARG1(1,STR), NULL, SMP_T_STR, SMP_USE_CONST }, |
5742 | | { "date", smp_fetch_date, ARG2(0,SINT,STR), smp_check_date_unit, SMP_T_SINT, SMP_USE_CONST }, |
5743 | | { "date_us", smp_fetch_date_us, 0, NULL, SMP_T_SINT, SMP_USE_CONST }, |
5744 | | { "hostname", smp_fetch_hostname, 0, NULL, SMP_T_STR, SMP_USE_CONST }, |
5745 | | { "nbproc", smp_fetch_nbproc,0, NULL, SMP_T_SINT, SMP_USE_CONST }, |
5746 | | { "pid", smp_fetch_pid, 0, NULL, SMP_T_SINT, SMP_USE_CONST }, |
5747 | | { "proc", smp_fetch_proc, 0, NULL, SMP_T_SINT, SMP_USE_CONST }, |
5748 | | { "quic_enabled", smp_fetch_quic_enabled, 0, NULL, SMP_T_BOOL, SMP_USE_CONST }, |
5749 | | { "thread", smp_fetch_thread, 0, NULL, SMP_T_SINT, SMP_USE_CONST }, |
5750 | | { "tgroup", smp_fetch_tgroup, 0, NULL, SMP_T_SINT, SMP_USE_CONST }, |
5751 | | { "rand", smp_fetch_rand, ARG1(0,SINT), NULL, SMP_T_SINT, SMP_USE_CONST }, |
5752 | | { "stopping", smp_fetch_stopping, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN }, |
5753 | | { "uptime", smp_fetch_uptime, 0, NULL, SMP_T_SINT, SMP_USE_CONST }, |
5754 | | { "uuid", smp_fetch_uuid, ARG1(0, SINT), smp_check_uuid, SMP_T_STR, SMP_USE_CONST }, |
5755 | | |
5756 | | { "cpu_calls", smp_fetch_cpu_calls, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, |
5757 | | { "cpu_ns_avg", smp_fetch_cpu_ns_avg, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, |
5758 | | { "cpu_ns_tot", smp_fetch_cpu_ns_tot, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, |
5759 | | { "cpu_usage_grp", smp_fetch_cpu_usage_grp, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, |
5760 | | { "cpu_usage_proc",smp_fetch_cpu_usage_proc, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, |
5761 | | { "cpu_usage_thr", smp_fetch_cpu_usage_thr, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, |
5762 | | { "lat_ns_avg", smp_fetch_lat_ns_avg, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, |
5763 | | { "lat_ns_tot", smp_fetch_lat_ns_tot, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, |
5764 | | |
5765 | | { "str", smp_fetch_const_str, ARG1(1,STR), NULL , SMP_T_STR, SMP_USE_CONST }, |
5766 | | { "bool", smp_fetch_const_bool, ARG1(1,STR), smp_check_const_bool, SMP_T_BOOL, SMP_USE_CONST }, |
5767 | | { "int", smp_fetch_const_int, ARG1(1,SINT), NULL , SMP_T_SINT, SMP_USE_CONST }, |
5768 | | { "ipv4", smp_fetch_const_ipv4, ARG1(1,IPV4), NULL , SMP_T_IPV4, SMP_USE_CONST }, |
5769 | | { "ipv6", smp_fetch_const_ipv6, ARG1(1,IPV6), NULL , SMP_T_IPV6, SMP_USE_CONST }, |
5770 | | { "bin", smp_fetch_const_bin, ARG1(1,STR), smp_check_const_bin , SMP_T_BIN, SMP_USE_CONST }, |
5771 | | { "meth", smp_fetch_const_meth, ARG1(1,STR), smp_check_const_meth, SMP_T_METH, SMP_USE_CONST }, |
5772 | | |
5773 | | { /* END */ }, |
5774 | | }}; |
5775 | | |
5776 | | INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws); |
5777 | | |
5778 | | /* Note: must not be declared <const> as its list will be overwritten */ |
5779 | | static struct sample_conv_kw_list sample_conv_kws = {ILH, { |
5780 | | { "add_item",sample_conv_add_item, ARG3(2,STR,STR,STR), smp_check_add_item, SMP_T_STR, SMP_T_STR }, |
5781 | | { "debug", sample_conv_debug, ARG2(0,STR,STR), smp_check_debug, SMP_T_ANY, SMP_T_SAME }, |
5782 | | { "b64dec", sample_conv_base642bin, 0, NULL, SMP_T_STR, SMP_T_BIN }, |
5783 | | { "base64", sample_conv_bin2base64, 0, NULL, SMP_T_BIN, SMP_T_STR }, |
5784 | | { "concat", sample_conv_concat, ARG3(1,STR,STR,STR), smp_check_concat, SMP_T_STR, SMP_T_STR }, |
5785 | | { "ub64enc", sample_conv_bin2base64url,0, NULL, SMP_T_BIN, SMP_T_STR }, |
5786 | | { "ub64dec", sample_conv_base64url2bin,0, NULL, SMP_T_STR, SMP_T_BIN }, |
5787 | | { "upper", sample_conv_str2upper, 0, NULL, SMP_T_STR, SMP_T_STR }, |
5788 | | { "lower", sample_conv_str2lower, 0, NULL, SMP_T_STR, SMP_T_STR }, |
5789 | | { "length", sample_conv_length, 0, NULL, SMP_T_STR, SMP_T_SINT }, |
5790 | | { "base2", sample_conv_bin2base2, 0, NULL, SMP_T_BIN, SMP_T_STR }, |
5791 | | { "be2dec", sample_conv_be2dec, ARG3(1,STR,SINT,SINT), sample_conv_2dec_check, SMP_T_BIN, SMP_T_STR }, |
5792 | | { "le2dec", sample_conv_le2dec, ARG3(1,STR,SINT,SINT), sample_conv_2dec_check, SMP_T_BIN, SMP_T_STR }, |
5793 | | { "be2hex", sample_conv_be2hex, ARG3(1,STR,SINT,SINT), sample_conv_be2hex_check, SMP_T_BIN, SMP_T_STR }, |
5794 | | { "hex", sample_conv_bin2hex, 0, NULL, SMP_T_BIN, SMP_T_STR }, |
5795 | | { "hex2i", sample_conv_hex2int, 0, NULL, SMP_T_STR, SMP_T_SINT }, |
5796 | | { "ipmask", sample_conv_ipmask, ARG2(1,MSK4,MSK6), NULL, SMP_T_ADDR, SMP_T_ADDR }, |
5797 | | { "ltime", sample_conv_ltime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR }, |
5798 | | { "ms_ltime", sample_conv_ms_ltime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR }, |
5799 | | { "us_ltime", sample_conv_us_ltime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR }, |
5800 | | { "utime", sample_conv_utime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR }, |
5801 | | { "ms_utime", sample_conv_ms_utime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR }, |
5802 | | { "us_utime", sample_conv_us_utime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR }, |
5803 | | { "crc32", sample_conv_crc32, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT }, |
5804 | | { "crc32c", sample_conv_crc32c, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT }, |
5805 | | { "djb2", sample_conv_djb2, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT }, |
5806 | | { "sdbm", sample_conv_sdbm, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT }, |
5807 | | { "wt6", sample_conv_wt6, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT }, |
5808 | | { "xxh3", sample_conv_xxh3, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT }, |
5809 | | { "xxh32", sample_conv_xxh32, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT }, |
5810 | | { "xxh64", sample_conv_xxh64, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT }, |
5811 | | { "json", sample_conv_json, ARG1(1,STR), sample_conv_json_check, SMP_T_STR, SMP_T_STR }, |
5812 | | { "bytes", sample_conv_bytes, ARG2(1,STR,STR), sample_conv_bytes_check, SMP_T_BIN, SMP_T_BIN }, |
5813 | | { "field", sample_conv_field, ARG3(2,SINT,STR,SINT), sample_conv_field_check, SMP_T_STR, SMP_T_STR }, |
5814 | | { "word", sample_conv_word, ARG3(2,SINT,STR,SINT), sample_conv_field_check, SMP_T_STR, SMP_T_STR }, |
5815 | | { "param", sample_conv_param, ARG2(1,STR,STR), sample_conv_param_check, SMP_T_STR, SMP_T_STR }, |
5816 | | { "regsub", sample_conv_regsub, ARG3(2,REG,STR,STR), sample_conv_regsub_check, SMP_T_STR, SMP_T_STR }, |
5817 | | { "sha1", sample_conv_sha1, 0, NULL, SMP_T_BIN, SMP_T_BIN }, |
5818 | | { "strcmp", sample_conv_strcmp, ARG1(1,STR), smp_check_strcmp, SMP_T_STR, SMP_T_SINT }, |
5819 | | { "host_only", sample_conv_host_only, 0, NULL, SMP_T_STR, SMP_T_STR }, |
5820 | | { "port_only", sample_conv_port_only, 0, NULL, SMP_T_STR, SMP_T_SINT }, |
5821 | | { "reverse", sample_conv_reverse, 0, NULL, SMP_T_STR, SMP_T_STR }, |
5822 | | { "reverse_dom", sample_conv_reverse_dom, 0, NULL, SMP_T_STR, SMP_T_STR }, |
5823 | | |
5824 | | /* gRPC converters. */ |
5825 | | { "ungrpc", sample_conv_ungrpc, ARG2(1,PBUF_FNUM,STR), sample_conv_protobuf_check, SMP_T_BIN, SMP_T_BIN }, |
5826 | | { "protobuf", sample_conv_protobuf, ARG2(1,PBUF_FNUM,STR), sample_conv_protobuf_check, SMP_T_BIN, SMP_T_BIN }, |
5827 | | |
5828 | | /* FIX converters */ |
5829 | | { "fix_is_valid", sample_conv_fix_is_valid, 0, NULL, SMP_T_BIN, SMP_T_BOOL }, |
5830 | | { "fix_tag_value", sample_conv_fix_tag_value, ARG1(1,STR), sample_conv_fix_value_check, SMP_T_BIN, SMP_T_BIN }, |
5831 | | |
5832 | | /* MQTT converters */ |
5833 | | { "mqtt_is_valid", sample_conv_mqtt_is_valid, 0, NULL, SMP_T_BIN, SMP_T_BOOL }, |
5834 | | { "mqtt_field_value", sample_conv_mqtt_field_value, ARG2(2,STR,STR), sample_conv_mqtt_field_value_check, SMP_T_BIN, SMP_T_STR }, |
5835 | | |
5836 | | { "iif", sample_conv_iif, ARG2(2, STR, STR), NULL, SMP_T_BOOL, SMP_T_STR }, |
5837 | | |
5838 | | { "and", sample_conv_binary_and, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT }, |
5839 | | { "or", sample_conv_binary_or, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT }, |
5840 | | { "xor", sample_conv_binary_xor, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT }, |
5841 | | { "cpl", sample_conv_binary_cpl, 0, NULL, SMP_T_SINT, SMP_T_SINT }, |
5842 | | { "bool", sample_conv_arith_bool, 0, NULL, SMP_T_SINT, SMP_T_BOOL }, |
5843 | | { "not", sample_conv_arith_not, 0, NULL, SMP_T_SINT, SMP_T_BOOL }, |
5844 | | { "odd", sample_conv_arith_odd, 0, NULL, SMP_T_SINT, SMP_T_BOOL }, |
5845 | | { "even", sample_conv_arith_even, 0, NULL, SMP_T_SINT, SMP_T_BOOL }, |
5846 | | { "add", sample_conv_arith_add, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT }, |
5847 | | { "sub", sample_conv_arith_sub, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT }, |
5848 | | { "mul", sample_conv_arith_mul, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT }, |
5849 | | { "div", sample_conv_arith_div, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT }, |
5850 | | { "mod", sample_conv_arith_mod, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT }, |
5851 | | { "neg", sample_conv_arith_neg, 0, NULL, SMP_T_SINT, SMP_T_SINT }, |
5852 | | |
5853 | | { "htonl", sample_conv_htonl, 0, NULL, SMP_T_SINT, SMP_T_BIN }, |
5854 | | { "cut_crlf", sample_conv_cut_crlf, 0, NULL, SMP_T_STR, SMP_T_STR }, |
5855 | | { "ltrim", sample_conv_ltrim, ARG1(1,STR), NULL, SMP_T_STR, SMP_T_STR }, |
5856 | | { "rtrim", sample_conv_rtrim, ARG1(1,STR), NULL, SMP_T_STR, SMP_T_STR }, |
5857 | | { "json_query", sample_conv_json_query, ARG2(1,STR,STR), sample_check_json_query , SMP_T_STR, SMP_T_ANY }, |
5858 | | |
5859 | | #ifdef USE_OPENSSL |
5860 | | /* JSON Web Token converters */ |
5861 | | { "jwt_header_query", sample_conv_jwt_header_query, ARG2(0,STR,STR), sample_conv_jwt_query_check, SMP_T_BIN, SMP_T_ANY }, |
5862 | | { "jwt_payload_query", sample_conv_jwt_payload_query, ARG2(0,STR,STR), sample_conv_jwt_query_check, SMP_T_BIN, SMP_T_ANY }, |
5863 | | { "jwt_verify", sample_conv_jwt_verify, ARG2(2,STR,STR), sample_conv_jwt_verify_check, SMP_T_BIN, SMP_T_SINT }, |
5864 | | { "jwt_verify_cert", sample_conv_jwt_verify_cert, ARG2(2,STR,STR), sample_conv_jwt_verify_cert_check, SMP_T_BIN, SMP_T_SINT }, |
5865 | | #endif |
5866 | | { "when", sample_conv_when, ARG3(1,STR,STR,STR), check_when_cond, SMP_T_ANY, SMP_T_ANY }, |
5867 | | { NULL, NULL, 0, 0, 0 }, |
5868 | | }}; |
5869 | | |
5870 | | INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws); |