~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_ssl_module.c

Version: ~ [ nginx-0.8.20 ] ~ [ nginx-0.7.62 ] ~ [ nginx-0.6.39 ] ~

  1 
  2 /*
  3  * Copyright (C) Igor Sysoev
  4  */
  5 
  6 
  7 #include <ngx_config.h>
  8 #include <ngx_core.h>
  9 #include <ngx_http.h>
 10 
 11 
 12 typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
 13     ngx_pool_t *pool, ngx_str_t *s);
 14 
 15 
 16 #define NGX_DEFAULT_CIPHERS  "HIGH:!ADH:!MD5"
 17 
 18 
 19 static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
 20     ngx_http_variable_value_t *v, uintptr_t data);
 21 static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,
 22     ngx_http_variable_value_t *v, uintptr_t data);
 23 
 24 static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf);
 25 static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);
 26 static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
 27     void *parent, void *child);
 28 
 29 static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
 30     void *conf);
 31 static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
 32     void *conf);
 33 
 34 
 35 static ngx_conf_bitmask_t  ngx_http_ssl_protocols[] = {
 36     { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
 37     { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
 38     { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
 39     { ngx_null_string, 0 }
 40 };
 41 
 42 
 43 static ngx_conf_enum_t  ngx_http_ssl_verify[] = {
 44     { ngx_string("off"), 0 },
 45     { ngx_string("on"), 1 },
 46     { ngx_string("optional"), 2 },
 47     { ngx_null_string, 0 }
 48 };
 49 
 50 
 51 static ngx_command_t  ngx_http_ssl_commands[] = {
 52 
 53     { ngx_string("ssl"),
 54       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
 55       ngx_http_ssl_enable,
 56       NGX_HTTP_SRV_CONF_OFFSET,
 57       offsetof(ngx_http_ssl_srv_conf_t, enable),
 58       NULL },
 59 
 60     { ngx_string("ssl_certificate"),
 61       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
 62       ngx_conf_set_str_slot,
 63       NGX_HTTP_SRV_CONF_OFFSET,
 64       offsetof(ngx_http_ssl_srv_conf_t, certificate),
 65       NULL },
 66 
 67     { ngx_string("ssl_certificate_key"),
 68       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
 69       ngx_conf_set_str_slot,
 70       NGX_HTTP_SRV_CONF_OFFSET,
 71       offsetof(ngx_http_ssl_srv_conf_t, certificate_key),
 72       NULL },
 73 
 74     { ngx_string("ssl_dhparam"),
 75       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
 76       ngx_conf_set_str_slot,
 77       NGX_HTTP_SRV_CONF_OFFSET,
 78       offsetof(ngx_http_ssl_srv_conf_t, dhparam),
 79       NULL },
 80 
 81     { ngx_string("ssl_protocols"),
 82       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
 83       ngx_conf_set_bitmask_slot,
 84       NGX_HTTP_SRV_CONF_OFFSET,
 85       offsetof(ngx_http_ssl_srv_conf_t, protocols),
 86       &ngx_http_ssl_protocols },
 87 
 88     { ngx_string("ssl_ciphers"),
 89       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
 90       ngx_conf_set_str_slot,
 91       NGX_HTTP_SRV_CONF_OFFSET,
 92       offsetof(ngx_http_ssl_srv_conf_t, ciphers),
 93       NULL },
 94 
 95     { ngx_string("ssl_verify_client"),
 96       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
 97       ngx_conf_set_enum_slot,
 98       NGX_HTTP_SRV_CONF_OFFSET,
 99       offsetof(ngx_http_ssl_srv_conf_t, verify),
100       &ngx_http_ssl_verify },
101 
102     { ngx_string("ssl_verify_depth"),
103       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
104       ngx_conf_set_num_slot,
105       NGX_HTTP_SRV_CONF_OFFSET,
106       offsetof(ngx_http_ssl_srv_conf_t, verify_depth),
107       NULL },
108 
109     { ngx_string("ssl_client_certificate"),
110       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
111       ngx_conf_set_str_slot,
112       NGX_HTTP_SRV_CONF_OFFSET,
113       offsetof(ngx_http_ssl_srv_conf_t, client_certificate),
114       NULL },
115 
116     { ngx_string("ssl_prefer_server_ciphers"),
117       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
118       ngx_conf_set_flag_slot,
119       NGX_HTTP_SRV_CONF_OFFSET,
120       offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers),
121       NULL },
122 
123     { ngx_string("ssl_session_cache"),
124       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,
125       ngx_http_ssl_session_cache,
126       NGX_HTTP_SRV_CONF_OFFSET,
127       0,
128       NULL },
129 
130     { ngx_string("ssl_session_timeout"),
131       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
132       ngx_conf_set_sec_slot,
133       NGX_HTTP_SRV_CONF_OFFSET,
134       offsetof(ngx_http_ssl_srv_conf_t, session_timeout),
135       NULL },
136 
137     { ngx_string("ssl_crl"),
138       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
139       ngx_conf_set_str_slot,
140       NGX_HTTP_SRV_CONF_OFFSET,
141       offsetof(ngx_http_ssl_srv_conf_t, crl),
142       NULL },
143 
144       ngx_null_command
145 };
146 
147 
148 static ngx_http_module_t  ngx_http_ssl_module_ctx = {
149     ngx_http_ssl_add_variables,            /* preconfiguration */
150     NULL,                                  /* postconfiguration */
151 
152     NULL,                                  /* create main configuration */
153     NULL,                                  /* init main configuration */
154 
155     ngx_http_ssl_create_srv_conf,          /* create server configuration */
156     ngx_http_ssl_merge_srv_conf,           /* merge server configuration */
157 
158     NULL,                                  /* create location configuration */
159     NULL                                   /* merge location configuration */
160 };
161 
162 
163 ngx_module_t  ngx_http_ssl_module = {
164     NGX_MODULE_V1,
165     &ngx_http_ssl_module_ctx,              /* module context */
166     ngx_http_ssl_commands,                 /* module directives */
167     NGX_HTTP_MODULE,                       /* module type */
168     NULL,                                  /* init master */
169     NULL,                                  /* init module */
170     NULL,                                  /* init process */
171     NULL,                                  /* init thread */
172     NULL,                                  /* exit thread */
173     NULL,                                  /* exit process */
174     NULL,                                  /* exit master */
175     NGX_MODULE_V1_PADDING
176 };
177 
178 
179 static ngx_http_variable_t  ngx_http_ssl_vars[] = {
180 
181     { ngx_string("ssl_protocol"), NULL, ngx_http_ssl_static_variable,
182       (uintptr_t) ngx_ssl_get_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },
183 
184     { ngx_string("ssl_cipher"), NULL, ngx_http_ssl_static_variable,
185       (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
186 
187     { ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable,
188       (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },
189 
190     { ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,
191       (uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
192 
193     { ngx_string("ssl_client_raw_cert"), NULL, ngx_http_ssl_variable,
194       (uintptr_t) ngx_ssl_get_raw_certificate,
195       NGX_HTTP_VAR_CHANGEABLE, 0 },
196 
197     { ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_variable,
198       (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
199 
200     { ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_variable,
201       (uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
202 
203     { ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable,
204       (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 },
205 
206     { ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,
207       (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },
208 
209     { ngx_null_string, NULL, NULL, 0, 0, 0 }
210 };
211 
212 
213 static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP");
214 
215 
216 static ngx_int_t
217 ngx_http_ssl_static_variable(ngx_http_request_t *r,
218     ngx_http_variable_value_t *v, uintptr_t data)
219 {
220     ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;
221 
222     size_t     len;
223     ngx_str_t  s;
224 
225     if (r->connection->ssl) {
226 
227         (void) handler(r->connection, NULL, &s);
228 
229         v->data = s.data;
230 
231         for (len = 0; v->data[len]; len++) { /* void */ }
232 
233         v->len = len;
234         v->valid = 1;
235         v->no_cacheable = 0;
236         v->not_found = 0;
237 
238         return NGX_OK;
239     }
240 
241     v->not_found = 1;
242 
243     return NGX_OK;
244 }
245 
246 
247 static ngx_int_t
248 ngx_http_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
249     uintptr_t data)
250 {
251     ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;
252 
253     ngx_str_t  s;
254 
255     if (r->connection->ssl) {
256 
257         if (handler(r->connection, r->pool, &s) != NGX_OK) {
258             return NGX_ERROR;
259         }
260 
261         v->len = s.len;
262         v->data = s.data;
263 
264         if (v->len) {
265             v->valid = 1;
266             v->no_cacheable = 0;
267             v->not_found = 0;
268 
269             return NGX_OK;
270         }
271     }
272 
273     v->not_found = 1;
274 
275     return NGX_OK;
276 }
277 
278 
279 static ngx_int_t
280 ngx_http_ssl_add_variables(ngx_conf_t *cf)
281 {
282     ngx_http_variable_t  *var, *v;
283 
284     for (v = ngx_http_ssl_vars; v->name.len; v++) {
285         var = ngx_http_add_variable(cf, &v->name, v->flags);
286         if (var == NULL) {
287             return NGX_ERROR;
288         }
289 
290         var->get_handler = v->get_handler;
291         var->data = v->data;
292     }
293 
294     return NGX_OK;
295 }
296 
297 
298 static void *
299 ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
300 {
301     ngx_http_ssl_srv_conf_t  *sscf;
302 
303     sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));
304     if (sscf == NULL) {
305         return NULL;
306     }
307 
308     /*
309      * set by ngx_pcalloc():
310      *
311      *     sscf->protocols = 0;
312      *     sscf->certificate = { 0, NULL };
313      *     sscf->certificate_key = { 0, NULL };
314      *     sscf->dhparam = { 0, NULL };
315      *     sscf->client_certificate = { 0, NULL };
316      *     sscf->crl = { 0, NULL };
317      *     sscf->ciphers.len = 0;
318      *     sscf->ciphers.data = NULL;
319      *     sscf->shm_zone = NULL;
320      */
321 
322     sscf->enable = NGX_CONF_UNSET;
323     sscf->prefer_server_ciphers = NGX_CONF_UNSET;
324     sscf->verify = NGX_CONF_UNSET_UINT;
325     sscf->verify_depth = NGX_CONF_UNSET_UINT;
326     sscf->builtin_session_cache = NGX_CONF_UNSET;
327     sscf->session_timeout = NGX_CONF_UNSET;
328 
329     return sscf;
330 }
331 
332 
333 static char *
334 ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
335 {
336     ngx_http_ssl_srv_conf_t *prev = parent;
337     ngx_http_ssl_srv_conf_t *conf = child;
338 
339     ngx_pool_cleanup_t  *cln;
340 
341     ngx_conf_merge_value(conf->enable, prev->enable, 0);
342 
343     ngx_conf_merge_value(conf->session_timeout,
344                          prev->session_timeout, 300);
345 
346     ngx_conf_merge_value(conf->prefer_server_ciphers,
347                          prev->prefer_server_ciphers, 0);
348 
349     ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
350                          (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1));
351 
352     ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
353     ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
354 
355     ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
356     ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
357 
358     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
359 
360     ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
361                          "");
362     ngx_conf_merge_str_value(conf->crl, prev->crl, "");
363 
364     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
365 
366 
367     conf->ssl.log = cf->log;
368 
369     if (conf->enable) {
370 
371         if (conf->certificate.len == 0) {
372             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
373                           "no \"ssl_certificate\" is defined for "
374                           "the \"ssl\" directive in %s:%ui",
375                           conf->file, conf->line);
376             return NGX_CONF_ERROR;
377         }
378 
379         if (conf->certificate_key.len == 0) {
380             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
381                           "no \"ssl_certificate_key\" is defined for "
382                           "the \"ssl\" directive in %s:%ui",
383                           conf->file, conf->line);
384             return NGX_CONF_ERROR;
385         }
386 
387     } else {
388 
389         if (conf->certificate.len == 0) {
390             return NGX_CONF_OK;
391         }
392 
393         if (conf->certificate_key.len == 0) {
394             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
395                           "no \"ssl_certificate_key\" is defined "
396                           "for certificate \"%V\"", &conf->certificate);
397             return NGX_CONF_ERROR;
398         }
399     }
400 
401     if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
402         return NGX_CONF_ERROR;
403     }
404 
405 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
406 
407     if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
408                                                ngx_http_ssl_servername)
409         == 0)
410     {
411         ngx_log_error(NGX_LOG_WARN, cf->log, 0,
412             "nginx was build with SNI support, however, now it is linked "
413             "dynamically to an OpenSSL library which has no tlsext support, "
414             "therefore SNI is not available");
415     }
416 
417 #endif
418 
419     cln = ngx_pool_cleanup_add(cf->pool, 0);
420     if (cln == NULL) {
421         return NGX_CONF_ERROR;
422     }
423 
424     cln->handler = ngx_ssl_cleanup_ctx;
425     cln->data = &conf->ssl;
426 
427     if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
428                             &conf->certificate_key)
429         != NGX_OK)
430     {
431         return NGX_CONF_ERROR;
432     }
433 
434     if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
435                                 (const char *) conf->ciphers.data)
436         == 0)
437     {
438         ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
439                       "SSL_CTX_set_cipher_list(\"%V\") failed",
440                       &conf->ciphers);
441     }
442 
443     if (conf->verify) {
444 
445         if (conf->client_certificate.len == 0) {
446             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
447                           "no ssl_client_certificate for ssl_client_verify");
448             return NGX_CONF_ERROR;
449         }
450 
451         if (ngx_ssl_client_certificate(cf, &conf->ssl,
452                                        &conf->client_certificate,
453                                        conf->verify_depth)
454             != NGX_OK)
455         {
456             return NGX_CONF_ERROR;
457         }
458 
459         if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
460             return NGX_CONF_ERROR;
461         }
462     }
463 
464     if (conf->prefer_server_ciphers) {
465         SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
466     }
467 
468     /* a temporary 512-bit RSA key is required for export versions of MSIE */
469     if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) {
470         return NGX_CONF_ERROR;
471     }
472 
473     if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
474         return NGX_CONF_ERROR;
475     }
476 
477     ngx_conf_merge_value(conf->builtin_session_cache,
478                          prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
479 
480     if (conf->shm_zone == NULL) {
481         conf->shm_zone = prev->shm_zone;
482     }
483 
484     if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,
485                               conf->builtin_session_cache,
486                               conf->shm_zone, conf->session_timeout)
487         != NGX_OK)
488     {
489         return NGX_CONF_ERROR;
490     }
491 
492     return NGX_CONF_OK;
493 }
494 
495 
496 static char *
497 ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
498 {
499     ngx_http_ssl_srv_conf_t *sscf = conf;
500 
501     char  *rv;
502 
503     rv = ngx_conf_set_flag_slot(cf, cmd, conf);
504 
505     if (rv != NGX_CONF_OK) {
506         return rv;
507     }
508 
509     sscf->file = cf->conf_file->file.name.data;
510     sscf->line = cf->conf_file->line;
511 
512     return NGX_CONF_OK;
513 }
514 
515 
516 static char *
517 ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
518 {
519     ngx_http_ssl_srv_conf_t *sscf = conf;
520 
521     size_t       len;
522     ngx_str_t   *value, name, size;
523     ngx_int_t    n;
524     ngx_uint_t   i, j;
525 
526     value = cf->args->elts;
527 
528     for (i = 1; i < cf->args->nelts; i++) {
529 
530         if (ngx_strcmp(value[i].data, "off") == 0) {
531             sscf->builtin_session_cache = NGX_SSL_NO_SCACHE;
532             continue;
533         }
534 
535         if (ngx_strcmp(value[i].data, "none") == 0) {
536             sscf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
537             continue;
538         }
539 
540         if (ngx_strcmp(value[i].data, "builtin") == 0) {
541             sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
542             continue;
543         }
544 
545         if (value[i].len > sizeof("builtin:") - 1
546             && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
547                == 0)
548         {
549             n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
550                          value[i].len - (sizeof("builtin:") - 1));
551 
552             if (n == NGX_ERROR) {
553                 goto invalid;
554             }
555 
556             sscf->builtin_session_cache = n;
557 
558             continue;
559         }
560 
561         if (value[i].len > sizeof("shared:") - 1
562             && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
563                == 0)
564         {
565             len = 0;
566 
567             for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
568                 if (value[i].data[j] == ':') {
569                     value[i].data[j] = '\0';
570                     break;
571                 }
572 
573                 len++;
574             }
575 
576             if (len == 0) {
577                 goto invalid;
578             }
579 
580             name.len = len;
581             name.data = value[i].data + sizeof("shared:") - 1;
582 
583             size.len = value[i].len - j - 1;
584             size.data = name.data + len + 1;
585 
586             n = ngx_parse_size(&size);
587 
588             if (n == NGX_ERROR) {
589                 goto invalid;
590             }
591 
592             if (n < (ngx_int_t) (8 * ngx_pagesize)) {
593                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
594                                    "session cache \"%V\" is too small",
595                                    &value[i]);
596 
597                 return NGX_CONF_ERROR;
598             }
599 
600             sscf->shm_zone = ngx_shared_memory_add(cf, &name, n,
601                                                    &ngx_http_ssl_module);
602             if (sscf->shm_zone == NULL) {
603                 return NGX_CONF_ERROR;
604             }
605 
606             continue;
607         }
608 
609         goto invalid;
610     }
611 
612     if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) {
613         sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
614     }
615 
616     return NGX_CONF_OK;
617 
618 invalid:
619 
620     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
621                        "invalid session cache \"%V\"", &value[i]);
622 
623     return NGX_CONF_ERROR;
624 }
625 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.