Mercurial > hg > nginx-ranges
comparison src/http/modules/ngx_http_userid_filter_module.c @ 88:e916a291e9aa NGINX_0_1_44
nginx 0.1.44
*) Feature: the IMAP/POP3 proxy supports SSL.
*) Feature: the "proxy_timeout" directive of the ngx_imap_proxy_module.
*) Feature: the "userid_mark" directive.
*) Feature: the $remote_user variable value is determined independently
of authorization use.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Tue, 06 Sep 2005 00:00:00 +0400 |
parents | b55cbf18157e |
children | 71c46860eb55 |
comparison
equal
deleted
inserted
replaced
87:5b7ec80c3c40 | 88:e916a291e9aa |
---|---|
24 ngx_int_t service; | 24 ngx_int_t service; |
25 | 25 |
26 ngx_str_t name; | 26 ngx_str_t name; |
27 ngx_str_t domain; | 27 ngx_str_t domain; |
28 ngx_str_t path; | 28 ngx_str_t path; |
29 ngx_str_t p3p; | |
30 | |
29 time_t expires; | 31 time_t expires; |
30 ngx_str_t p3p; | 32 |
33 u_char mark; | |
31 } ngx_http_userid_conf_t; | 34 } ngx_http_userid_conf_t; |
32 | 35 |
33 | 36 |
34 typedef struct { | 37 typedef struct { |
35 uint32_t uid_got[4]; | 38 uint32_t uid_got[4]; |
36 uint32_t uid_set[4]; | 39 uint32_t uid_set[4]; |
40 ngx_str_t cookie; | |
37 } ngx_http_userid_ctx_t; | 41 } ngx_http_userid_ctx_t; |
38 | 42 |
39 | 43 |
40 static ngx_int_t ngx_http_userid_get_uid(ngx_http_request_t *r, | 44 static void ngx_http_userid_get_uid(ngx_http_request_t *r, |
41 ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf); | 45 ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf); |
42 static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r, | 46 static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r, |
43 ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf); | 47 ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf); |
44 | 48 |
45 static size_t ngx_http_userid_log_uid_got_getlen(ngx_http_request_t *r, | 49 static size_t ngx_http_userid_log_uid_got_getlen(ngx_http_request_t *r, |
59 static char *ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data); | 63 static char *ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data); |
60 static char *ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data); | 64 static char *ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data); |
61 static char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, | 65 static char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, |
62 void *conf); | 66 void *conf); |
63 static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data); | 67 static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data); |
68 static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, | |
69 void *conf); | |
70 | |
64 | 71 |
65 | 72 |
66 static uint32_t sequencer_v1 = 1; | 73 static uint32_t sequencer_v1 = 1; |
67 static uint32_t sequencer_v2 = 0x03030302; | 74 static uint32_t sequencer_v2 = 0x03030302; |
68 | 75 |
82 }; | 89 }; |
83 | 90 |
84 | 91 |
85 static ngx_conf_post_handler_pt ngx_http_userid_domain_p = | 92 static ngx_conf_post_handler_pt ngx_http_userid_domain_p = |
86 ngx_http_userid_domain; | 93 ngx_http_userid_domain; |
87 | |
88 static ngx_conf_post_handler_pt ngx_http_userid_path_p = ngx_http_userid_path; | 94 static ngx_conf_post_handler_pt ngx_http_userid_path_p = ngx_http_userid_path; |
89 static ngx_conf_post_handler_pt ngx_http_userid_p3p_p = ngx_http_userid_p3p; | 95 static ngx_conf_post_handler_pt ngx_http_userid_p3p_p = ngx_http_userid_p3p; |
90 | 96 |
91 | 97 |
92 static ngx_command_t ngx_http_userid_commands[] = { | 98 static ngx_command_t ngx_http_userid_commands[] = { |
97 NGX_HTTP_LOC_CONF_OFFSET, | 103 NGX_HTTP_LOC_CONF_OFFSET, |
98 offsetof(ngx_http_userid_conf_t, enable), | 104 offsetof(ngx_http_userid_conf_t, enable), |
99 ngx_http_userid_state }, | 105 ngx_http_userid_state }, |
100 | 106 |
101 { ngx_string("userid_service"), | 107 { ngx_string("userid_service"), |
102 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | 108 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
103 ngx_conf_set_num_slot, | 109 ngx_conf_set_num_slot, |
104 NGX_HTTP_LOC_CONF_OFFSET, | 110 NGX_HTTP_LOC_CONF_OFFSET, |
105 offsetof(ngx_http_userid_conf_t, service), | 111 offsetof(ngx_http_userid_conf_t, service), |
106 NULL }, | 112 NULL }, |
107 | 113 |
108 { ngx_string("userid_name"), | 114 { ngx_string("userid_name"), |
109 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | 115 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
110 ngx_conf_set_str_slot, | 116 ngx_conf_set_str_slot, |
111 NGX_HTTP_LOC_CONF_OFFSET, | 117 NGX_HTTP_LOC_CONF_OFFSET, |
112 offsetof(ngx_http_userid_conf_t, name), | 118 offsetof(ngx_http_userid_conf_t, name), |
113 NULL }, | 119 NULL }, |
114 | 120 |
115 { ngx_string("userid_domain"), | 121 { ngx_string("userid_domain"), |
116 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | 122 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
117 ngx_conf_set_str_slot, | 123 ngx_conf_set_str_slot, |
118 NGX_HTTP_LOC_CONF_OFFSET, | 124 NGX_HTTP_LOC_CONF_OFFSET, |
119 offsetof(ngx_http_userid_conf_t, domain), | 125 offsetof(ngx_http_userid_conf_t, domain), |
120 &ngx_http_userid_domain_p }, | 126 &ngx_http_userid_domain_p }, |
121 | 127 |
122 { ngx_string("userid_path"), | 128 { ngx_string("userid_path"), |
123 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | 129 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
124 ngx_conf_set_str_slot, | 130 ngx_conf_set_str_slot, |
125 NGX_HTTP_LOC_CONF_OFFSET, | 131 NGX_HTTP_LOC_CONF_OFFSET, |
126 offsetof(ngx_http_userid_conf_t, path), | 132 offsetof(ngx_http_userid_conf_t, path), |
127 &ngx_http_userid_path_p }, | 133 &ngx_http_userid_path_p }, |
128 | 134 |
137 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | 143 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
138 ngx_conf_set_str_slot, | 144 ngx_conf_set_str_slot, |
139 NGX_HTTP_LOC_CONF_OFFSET, | 145 NGX_HTTP_LOC_CONF_OFFSET, |
140 offsetof(ngx_http_userid_conf_t, p3p), | 146 offsetof(ngx_http_userid_conf_t, p3p), |
141 &ngx_http_userid_p3p_p }, | 147 &ngx_http_userid_p3p_p }, |
148 | |
149 { ngx_string("userid_mark"), | |
150 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
151 ngx_http_userid_mark, | |
152 NGX_HTTP_LOC_CONF_OFFSET, | |
153 0, | |
154 NULL }, | |
142 | 155 |
143 ngx_null_command | 156 ngx_null_command |
144 }; | 157 }; |
145 | 158 |
146 | 159 |
203 return NGX_ERROR; | 216 return NGX_ERROR; |
204 } | 217 } |
205 | 218 |
206 ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module); | 219 ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module); |
207 | 220 |
208 | 221 ngx_http_userid_get_uid(r, ctx, conf); |
209 rc = ngx_http_userid_get_uid(r, ctx, conf); | 222 |
223 if (conf->enable == NGX_HTTP_USERID_LOG) { | |
224 return ngx_http_next_header_filter(r); | |
225 } | |
226 | |
227 if (ctx->uid_got[3] != 0) { | |
228 if (conf->mark == '\0') { | |
229 return ngx_http_next_header_filter(r); | |
230 | |
231 } else { | |
232 if (ctx->cookie.len > 23 | |
233 && ctx->cookie.data[22] == conf->mark | |
234 && ctx->cookie.data[23] == '=') | |
235 { | |
236 return ngx_http_next_header_filter(r); | |
237 } | |
238 } | |
239 } | |
240 | |
241 rc = ngx_http_userid_set_uid(r, ctx, conf); | |
210 | 242 |
211 if (rc != NGX_OK) { | 243 if (rc != NGX_OK) { |
212 return rc; | 244 return rc; |
213 } | 245 } |
214 | 246 |
215 if (conf->enable == NGX_HTTP_USERID_LOG || ctx->uid_got[3] != 0) { | |
216 return ngx_http_next_header_filter(r); | |
217 } | |
218 | |
219 rc = ngx_http_userid_set_uid(r, ctx, conf); | |
220 | |
221 if (rc != NGX_OK) { | |
222 return rc; | |
223 } | |
224 | |
225 return ngx_http_next_header_filter(r); | 247 return ngx_http_next_header_filter(r); |
226 } | 248 } |
227 | 249 |
228 | 250 |
229 static ngx_int_t | 251 static void |
230 ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, | 252 ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, |
231 ngx_http_userid_conf_t *conf) | 253 ngx_http_userid_conf_t *conf) |
232 { | 254 { |
233 ngx_int_t n; | 255 ngx_int_t n; |
234 ngx_str_t src, dst; | 256 ngx_str_t src, dst; |
235 ngx_table_elt_t **cookies; | 257 ngx_table_elt_t **cookies; |
236 | 258 |
237 n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name, | 259 n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name, |
238 &src); | 260 &ctx->cookie); |
239 if (n == NGX_DECLINED) { | 261 if (n == NGX_DECLINED) { |
240 return NGX_OK; | 262 return; |
241 } | 263 } |
242 | 264 |
243 if (src.len < 22) { | 265 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
266 "uid cookie: \"%V\"", &ctx->cookie); | |
267 | |
268 if (ctx->cookie.len < 22) { | |
244 cookies = r->headers_in.cookies.elts; | 269 cookies = r->headers_in.cookies.elts; |
245 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | 270 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
246 "client sent too short userid cookie \"%V\"", | 271 "client sent too short userid cookie \"%V\"", |
247 &cookies[n]->value); | 272 &cookies[n]->value); |
248 return NGX_OK; | 273 return; |
249 } | 274 } |
275 | |
276 src = ctx->cookie; | |
250 | 277 |
251 /* | 278 /* |
252 * we have to limit encoded string to 22 characters | 279 * we have to limit the encoded string to 22 characters because |
253 * because there are already the millions cookies with a garbage | 280 * 1) cookie may be marked by "userid_mark", |
254 * instead of the correct base64 trail "==" | 281 * 2) and there are already the millions cookies with a garbage |
282 * instead of the correct base64 trail "==" | |
255 */ | 283 */ |
256 | 284 |
257 src.len = 22; | 285 src.len = 22; |
258 | 286 |
259 dst.data = (u_char *) ctx->uid_got; | 287 dst.data = (u_char *) ctx->uid_got; |
261 if (ngx_decode_base64(&dst, &src) == NGX_ERROR) { | 289 if (ngx_decode_base64(&dst, &src) == NGX_ERROR) { |
262 cookies = r->headers_in.cookies.elts; | 290 cookies = r->headers_in.cookies.elts; |
263 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | 291 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
264 "client sent invalid userid cookie \"%V\"", | 292 "client sent invalid userid cookie \"%V\"", |
265 &cookies[n]->value); | 293 &cookies[n]->value); |
266 return NGX_OK; | 294 return; |
267 } | 295 } |
268 | 296 |
269 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 297 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
270 "uid: %08XD%08XD%08XD%08XD", | 298 "uid: %08XD%08XD%08XD%08XD", |
271 ctx->uid_got[0], ctx->uid_got[1], | 299 ctx->uid_got[0], ctx->uid_got[1], |
272 ctx->uid_got[2], ctx->uid_got[3]); | 300 ctx->uid_got[2], ctx->uid_got[3]); |
273 | |
274 return NGX_OK; | |
275 } | 301 } |
276 | 302 |
277 | 303 |
278 static ngx_int_t | 304 static ngx_int_t |
279 ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, | 305 ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, |
284 socklen_t slen; | 310 socklen_t slen; |
285 struct sockaddr_in sin; | 311 struct sockaddr_in sin; |
286 ngx_str_t src, dst; | 312 ngx_str_t src, dst; |
287 ngx_table_elt_t *set_cookie, *p3p; | 313 ngx_table_elt_t *set_cookie, *p3p; |
288 | 314 |
289 /* TODO: mutex for sequencers */ | 315 /* |
290 | 316 * TODO: in the threaded mode the sequencers should be in TLS and their |
291 if (conf->enable == NGX_HTTP_USERID_V1) { | 317 * ranges should be divided between threads |
292 if (conf->service == NGX_CONF_UNSET) { | 318 */ |
293 ctx->uid_set[0] = 0; | 319 |
320 if (ctx->uid_got[3] == 0) { | |
321 | |
322 if (conf->enable == NGX_HTTP_USERID_V1) { | |
323 if (conf->service == NGX_CONF_UNSET) { | |
324 ctx->uid_set[0] = 0; | |
325 } else { | |
326 ctx->uid_set[0] = conf->service; | |
327 } | |
328 ctx->uid_set[1] = ngx_time(); | |
329 ctx->uid_set[2] = ngx_pid; | |
330 ctx->uid_set[3] = sequencer_v1; | |
331 sequencer_v1 += 0x100; | |
332 | |
294 } else { | 333 } else { |
295 ctx->uid_set[0] = htonl(conf->service); | 334 if (conf->service == NGX_CONF_UNSET) { |
335 if (r->in_addr == 0) { | |
336 slen = sizeof(struct sockaddr_in); | |
337 if (getsockname(r->connection->fd, | |
338 (struct sockaddr *) &sin, &slen) == -1) | |
339 { | |
340 ngx_log_error(NGX_LOG_CRIT, r->connection->log, | |
341 ngx_socket_errno, "getsockname() failed"); | |
342 } | |
343 | |
344 r->in_addr = sin.sin_addr.s_addr; | |
345 } | |
346 | |
347 ctx->uid_set[0] = htonl(r->in_addr); | |
348 | |
349 } else { | |
350 ctx->uid_set[0] = htonl(conf->service); | |
351 } | |
352 | |
353 ctx->uid_set[1] = htonl(ngx_time()); | |
354 ctx->uid_set[2] = htonl(ngx_pid); | |
355 ctx->uid_set[3] = htonl(sequencer_v2); | |
356 sequencer_v2 += 0x100; | |
357 if (sequencer_v2 < 0x03030302) { | |
358 sequencer_v2 = 0x03030302; | |
359 } | |
296 } | 360 } |
297 | 361 |
298 ctx->uid_set[1] = ngx_time(); | |
299 ctx->uid_set[2] = ngx_pid; | |
300 ctx->uid_set[3] = sequencer_v1; | |
301 sequencer_v1 += 0x100; | |
302 | |
303 } else { | 362 } else { |
304 if (conf->service == NGX_CONF_UNSET) { | 363 ctx->uid_set[0] = ctx->uid_got[0]; |
305 if (r->in_addr == 0) { | 364 ctx->uid_set[1] = ctx->uid_got[1]; |
306 slen = sizeof(struct sockaddr_in); | 365 ctx->uid_set[2] = ctx->uid_got[2]; |
307 if (getsockname(r->connection->fd, | 366 ctx->uid_set[3] = ctx->uid_got[3]; |
308 (struct sockaddr *) &sin, &slen) == -1) | |
309 { | |
310 ngx_log_error(NGX_LOG_CRIT, r->connection->log, | |
311 ngx_socket_errno, "getsockname() failed"); | |
312 } | |
313 | |
314 r->in_addr = sin.sin_addr.s_addr; | |
315 } | |
316 | |
317 ctx->uid_set[0] = htonl(r->in_addr); | |
318 | |
319 } else { | |
320 ctx->uid_set[0] = htonl(conf->service); | |
321 } | |
322 | |
323 ctx->uid_set[1] = htonl(ngx_time()); | |
324 ctx->uid_set[2] = htonl(ngx_pid); | |
325 ctx->uid_set[3] = htonl(sequencer_v2); | |
326 sequencer_v2 += 0x100; | |
327 if (sequencer_v2 < 0x03030302) { | |
328 sequencer_v2 = 0x03030302; | |
329 } | |
330 } | 367 } |
331 | 368 |
332 len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len; | 369 len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len; |
333 | 370 |
334 if (conf->expires) { | 371 if (conf->expires) { |
345 } | 382 } |
346 | 383 |
347 p = ngx_cpymem(cookie, conf->name.data, conf->name.len); | 384 p = ngx_cpymem(cookie, conf->name.data, conf->name.len); |
348 *p++ = '='; | 385 *p++ = '='; |
349 | 386 |
350 src.len = 16; | 387 if (ctx->uid_got[3] == 0) { |
351 src.data = (u_char *) ctx->uid_set; | 388 src.len = 16; |
352 dst.data = p; | 389 src.data = (u_char *) ctx->uid_set; |
353 | 390 dst.data = p; |
354 ngx_encode_base64(&dst, &src); | 391 |
355 | 392 ngx_encode_base64(&dst, &src); |
356 p += dst.len; | 393 |
394 p += dst.len; | |
395 | |
396 if (conf->mark) { | |
397 *(p - 2) = conf->mark; | |
398 } | |
399 | |
400 } else { | |
401 p = ngx_cpymem(p, ctx->cookie.data, 22); | |
402 *p++ = conf->mark; | |
403 *p++ = '='; | |
404 } | |
357 | 405 |
358 if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) { | 406 if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) { |
359 p = ngx_cpymem(p, expires, sizeof(expires) - 1); | 407 p = ngx_cpymem(p, expires, sizeof(expires) - 1); |
360 | 408 |
361 } else if (conf->expires) { | 409 } else if (conf->expires) { |
543 */ | 591 */ |
544 | 592 |
545 conf->enable = NGX_CONF_UNSET_UINT; | 593 conf->enable = NGX_CONF_UNSET_UINT; |
546 conf->service = NGX_CONF_UNSET; | 594 conf->service = NGX_CONF_UNSET; |
547 conf->expires = NGX_CONF_UNSET; | 595 conf->expires = NGX_CONF_UNSET; |
596 conf->mark = (u_char) '\xFF'; | |
548 | 597 |
549 return conf; | 598 return conf; |
550 } | 599 } |
551 | 600 |
552 | 601 |
565 ngx_conf_merge_str_value(conf->p3p, prev->p3p, ""); | 614 ngx_conf_merge_str_value(conf->p3p, prev->p3p, ""); |
566 | 615 |
567 ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET); | 616 ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET); |
568 ngx_conf_merge_sec_value(conf->expires, prev->expires, 0); | 617 ngx_conf_merge_sec_value(conf->expires, prev->expires, 0); |
569 | 618 |
619 if (conf->mark == (u_char) '\xFF') { | |
620 if (prev->mark == (u_char) '\xFF') { | |
621 conf->mark = '\0'; | |
622 } else { | |
623 conf->mark = prev->mark; | |
624 } | |
625 } | |
626 | |
570 return NGX_CONF_OK; | 627 return NGX_CONF_OK; |
571 } | 628 } |
572 | 629 |
573 | 630 |
574 static char * | 631 static char * |
625 static char * | 682 static char * |
626 ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | 683 ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
627 { | 684 { |
628 ngx_http_userid_conf_t *ucf = conf; | 685 ngx_http_userid_conf_t *ucf = conf; |
629 | 686 |
630 ngx_str_t *value; | 687 ngx_str_t *value; |
631 | 688 |
632 if (ucf->expires != NGX_CONF_UNSET) { | 689 if (ucf->expires != NGX_CONF_UNSET) { |
633 return "is duplicate"; | 690 return "is duplicate"; |
634 } | 691 } |
635 | 692 |
668 p3p->data = (u_char *) ""; | 725 p3p->data = (u_char *) ""; |
669 } | 726 } |
670 | 727 |
671 return NGX_CONF_OK; | 728 return NGX_CONF_OK; |
672 } | 729 } |
730 | |
731 | |
732 static char * | |
733 ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
734 { | |
735 ngx_http_userid_conf_t *ucf = conf; | |
736 | |
737 ngx_str_t *value; | |
738 | |
739 if (ucf->mark != (u_char) '\xFF') { | |
740 return "is duplicate"; | |
741 } | |
742 | |
743 value = cf->args->elts; | |
744 | |
745 if (ngx_strcmp(value[1].data, "off") == 0) { | |
746 ucf->mark = '\0'; | |
747 return NGX_CONF_OK; | |
748 } | |
749 | |
750 if (value[1].len != 1 | |
751 || !((value[1].data[0] >= '0' && value[1].data[0] <= '9') | |
752 || (value[1].data[0] >= 'A' && value[1].data[0] <= 'Z') | |
753 || (value[1].data[0] >= 'a' && value[1].data[0] <= 'z') | |
754 || value[1].data[0] == '=')) | |
755 { | |
756 return "value must be \"off\" or a single letter, digit or \"=\""; | |
757 } | |
758 | |
759 ucf->mark = value[1].data[0]; | |
760 | |
761 return NGX_CONF_OK; | |
762 } |