1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 #include <ngx_mail.h>
12 
13 
14 static void *ngx_mail_core_create_main_conf(ngx_conf_t *cf);
15 static void *ngx_mail_core_create_srv_conf(ngx_conf_t *cf);
16 static char *ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
17     void *child);
18 static char *ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
19     void *conf);
20 static char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
21     void *conf);
22 static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd,
23     void *conf);
24 static char *ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
25     void *conf);
26 static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
27     void *conf);
28 
29 
30 static ngx_command_t  ngx_mail_core_commands[] = {
31 
32     { ngx_string("server"),
33       NGX_MAIL_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
34       ngx_mail_core_server,
35       0,
36       0,
37       NULL },
38 
39     { ngx_string("listen"),
40       NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
41       ngx_mail_core_listen,
42       NGX_MAIL_SRV_CONF_OFFSET,
43       0,
44       NULL },
45 
46     { ngx_string("protocol"),
47       NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
48       ngx_mail_core_protocol,
49       NGX_MAIL_SRV_CONF_OFFSET,
50       0,
51       NULL },
52 
53     { ngx_string("timeout"),
54       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
55       ngx_conf_set_msec_slot,
56       NGX_MAIL_SRV_CONF_OFFSET,
57       offsetof(ngx_mail_core_srv_conf_t, timeout),
58       NULL },
59 
60     { ngx_string("server_name"),
61       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
62       ngx_conf_set_str_slot,
63       NGX_MAIL_SRV_CONF_OFFSET,
64       offsetof(ngx_mail_core_srv_conf_t, server_name),
65       NULL },
66 
67 #if (NGX_HAVE_FSTACK)
68     { ngx_string("kernel_network_stack"),
69       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
70       ngx_conf_set_flag_slot,
71       NGX_MAIL_SRV_CONF_OFFSET,
72       offsetof(ngx_mail_core_srv_conf_t, kernel_network_stack),
73       NULL },
74 #endif
75 
76     { ngx_string("error_log"),
77       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
78       ngx_mail_core_error_log,
79       NGX_MAIL_SRV_CONF_OFFSET,
80       0,
81       NULL },
82 
83     { ngx_string("resolver"),
84       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
85       ngx_mail_core_resolver,
86       NGX_MAIL_SRV_CONF_OFFSET,
87       0,
88       NULL },
89 
90     { ngx_string("resolver_timeout"),
91       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
92       ngx_conf_set_msec_slot,
93       NGX_MAIL_SRV_CONF_OFFSET,
94       offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),
95       NULL },
96 
97       ngx_null_command
98 };
99 
100 
101 static ngx_mail_module_t  ngx_mail_core_module_ctx = {
102     NULL,                                  /* protocol */
103 
104     ngx_mail_core_create_main_conf,        /* create main configuration */
105     NULL,                                  /* init main configuration */
106 
107     ngx_mail_core_create_srv_conf,         /* create server configuration */
108     ngx_mail_core_merge_srv_conf           /* merge server configuration */
109 };
110 
111 
112 ngx_module_t  ngx_mail_core_module = {
113     NGX_MODULE_V1,
114     &ngx_mail_core_module_ctx,             /* module context */
115     ngx_mail_core_commands,                /* module directives */
116     NGX_MAIL_MODULE,                       /* module type */
117     NULL,                                  /* init master */
118     NULL,                                  /* init module */
119     NULL,                                  /* init process */
120     NULL,                                  /* init thread */
121     NULL,                                  /* exit thread */
122     NULL,                                  /* exit process */
123     NULL,                                  /* exit master */
124     NGX_MODULE_V1_PADDING
125 };
126 
127 
128 static void *
ngx_mail_core_create_main_conf(ngx_conf_t * cf)129 ngx_mail_core_create_main_conf(ngx_conf_t *cf)
130 {
131     ngx_mail_core_main_conf_t  *cmcf;
132 
133     cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t));
134     if (cmcf == NULL) {
135         return NULL;
136     }
137 
138     if (ngx_array_init(&cmcf->servers, cf->pool, 4,
139                        sizeof(ngx_mail_core_srv_conf_t *))
140         != NGX_OK)
141     {
142         return NULL;
143     }
144 
145     if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t))
146         != NGX_OK)
147     {
148         return NULL;
149     }
150 
151     return cmcf;
152 }
153 
154 
155 static void *
ngx_mail_core_create_srv_conf(ngx_conf_t * cf)156 ngx_mail_core_create_srv_conf(ngx_conf_t *cf)
157 {
158     ngx_mail_core_srv_conf_t  *cscf;
159 
160     cscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_srv_conf_t));
161     if (cscf == NULL) {
162         return NULL;
163     }
164 
165     /*
166      * set by ngx_pcalloc():
167      *
168      *     cscf->protocol = NULL;
169      *     cscf->error_log = NULL;
170      */
171 
172     cscf->timeout = NGX_CONF_UNSET_MSEC;
173     cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
174 
175     cscf->resolver = NGX_CONF_UNSET_PTR;
176 
177     cscf->file_name = cf->conf_file->file.name.data;
178     cscf->line = cf->conf_file->line;
179 
180 #if (NGX_HAVE_FSTACK)
181     cscf->kernel_network_stack = NGX_CONF_UNSET;
182 #endif
183 
184     return cscf;
185 }
186 
187 
188 static char *
ngx_mail_core_merge_srv_conf(ngx_conf_t * cf,void * parent,void * child)189 ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
190 {
191     ngx_mail_core_srv_conf_t *prev = parent;
192     ngx_mail_core_srv_conf_t *conf = child;
193 
194     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
195     ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,
196                               30000);
197 
198 
199     ngx_conf_merge_str_value(conf->server_name, prev->server_name, "");
200 
201     if (conf->server_name.len == 0) {
202         conf->server_name = cf->cycle->hostname;
203     }
204 
205     if (conf->protocol == NULL) {
206         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
207                       "unknown mail protocol for server in %s:%ui",
208                       conf->file_name, conf->line);
209         return NGX_CONF_ERROR;
210     }
211 
212     if (conf->error_log == NULL) {
213         if (prev->error_log) {
214             conf->error_log = prev->error_log;
215         } else {
216             conf->error_log = &cf->cycle->new_log;
217         }
218     }
219 
220     ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);
221 
222 #if (NGX_HAVE_FSTACK)
223     /* By default, we set up a server on fstack */
224     ngx_conf_merge_value(conf->kernel_network_stack,
225                               prev->kernel_network_stack, 0);
226 #endif
227 
228     return NGX_CONF_OK;
229 }
230 
231 
232 static char *
ngx_mail_core_server(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)233 ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
234 {
235     char                       *rv;
236     void                       *mconf;
237     ngx_uint_t                  m;
238     ngx_conf_t                  pcf;
239     ngx_mail_module_t          *module;
240     ngx_mail_conf_ctx_t        *ctx, *mail_ctx;
241     ngx_mail_core_srv_conf_t   *cscf, **cscfp;
242     ngx_mail_core_main_conf_t  *cmcf;
243 
244     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));
245     if (ctx == NULL) {
246         return NGX_CONF_ERROR;
247     }
248 
249     mail_ctx = cf->ctx;
250     ctx->main_conf = mail_ctx->main_conf;
251 
252     /* the server{}'s srv_conf */
253 
254     ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);
255     if (ctx->srv_conf == NULL) {
256         return NGX_CONF_ERROR;
257     }
258 
259     for (m = 0; cf->cycle->modules[m]; m++) {
260         if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {
261             continue;
262         }
263 
264         module = cf->cycle->modules[m]->ctx;
265 
266         if (module->create_srv_conf) {
267             mconf = module->create_srv_conf(cf);
268             if (mconf == NULL) {
269                 return NGX_CONF_ERROR;
270             }
271 
272             ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;
273         }
274     }
275 
276     /* the server configuration context */
277 
278     cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index];
279     cscf->ctx = ctx;
280 
281     cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];
282 
283     cscfp = ngx_array_push(&cmcf->servers);
284     if (cscfp == NULL) {
285         return NGX_CONF_ERROR;
286     }
287 
288     *cscfp = cscf;
289 
290 
291     /* parse inside server{} */
292 
293     pcf = *cf;
294     cf->ctx = ctx;
295     cf->cmd_type = NGX_MAIL_SRV_CONF;
296 
297     rv = ngx_conf_parse(cf, NULL);
298 
299     *cf = pcf;
300 
301     if (rv == NGX_CONF_OK && !cscf->listen) {
302         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
303                       "no \"listen\" is defined for server in %s:%ui",
304                       cscf->file_name, cscf->line);
305         return NGX_CONF_ERROR;
306     }
307 
308     return rv;
309 }
310 
311 
312 static char *
ngx_mail_core_listen(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)313 ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
314 {
315     ngx_mail_core_srv_conf_t  *cscf = conf;
316 
317     ngx_str_t                  *value, size;
318     ngx_url_t                   u;
319     ngx_uint_t                  i, n, m;
320     ngx_mail_listen_t          *ls, *als;
321     ngx_mail_module_t          *module;
322     ngx_mail_core_main_conf_t  *cmcf;
323 
324     cscf->listen = 1;
325 
326     value = cf->args->elts;
327 
328     ngx_memzero(&u, sizeof(ngx_url_t));
329 
330     u.url = value[1];
331     u.listen = 1;
332 
333     if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
334         if (u.err) {
335             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
336                                "%s in \"%V\" of the \"listen\" directive",
337                                u.err, &u.url);
338         }
339 
340         return NGX_CONF_ERROR;
341     }
342 
343     cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module);
344 
345     ls = ngx_array_push_n(&cmcf->listen, u.naddrs);
346     if (ls == NULL) {
347         return NGX_CONF_ERROR;
348     }
349 
350     ngx_memzero(ls, sizeof(ngx_mail_listen_t));
351 
352     ls->backlog = NGX_LISTEN_BACKLOG;
353     ls->rcvbuf = -1;
354     ls->sndbuf = -1;
355     ls->ctx = cf->ctx;
356 
357 #if (NGX_HAVE_INET6)
358     ls->ipv6only = 1;
359 #endif
360 
361     if (cscf->protocol == NULL) {
362         for (m = 0; cf->cycle->modules[m]; m++) {
363             if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {
364                 continue;
365             }
366 
367             module = cf->cycle->modules[m]->ctx;
368 
369             if (module->protocol == NULL) {
370                 continue;
371             }
372 
373             for (i = 0; module->protocol->port[i]; i++) {
374                 if (module->protocol->port[i] == u.port) {
375                     cscf->protocol = module->protocol;
376                     break;
377                 }
378             }
379         }
380     }
381 
382     for (i = 2; i < cf->args->nelts; i++) {
383 
384         if (ngx_strcmp(value[i].data, "bind") == 0) {
385             ls->bind = 1;
386             continue;
387         }
388 
389         if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) {
390             ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
391             ls->bind = 1;
392 
393             if (ls->backlog == NGX_ERROR || ls->backlog == 0) {
394                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
395                                    "invalid backlog \"%V\"", &value[i]);
396                 return NGX_CONF_ERROR;
397             }
398 
399             continue;
400         }
401 
402         if (ngx_strncmp(value[i].data, "rcvbuf=", 7) == 0) {
403             size.len = value[i].len - 7;
404             size.data = value[i].data + 7;
405 
406             ls->rcvbuf = ngx_parse_size(&size);
407             ls->bind = 1;
408 
409             if (ls->rcvbuf == NGX_ERROR) {
410                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
411                                    "invalid rcvbuf \"%V\"", &value[i]);
412                 return NGX_CONF_ERROR;
413             }
414 
415             continue;
416         }
417 
418         if (ngx_strncmp(value[i].data, "sndbuf=", 7) == 0) {
419             size.len = value[i].len - 7;
420             size.data = value[i].data + 7;
421 
422             ls->sndbuf = ngx_parse_size(&size);
423             ls->bind = 1;
424 
425             if (ls->sndbuf == NGX_ERROR) {
426                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
427                                    "invalid sndbuf \"%V\"", &value[i]);
428                 return NGX_CONF_ERROR;
429             }
430 
431             continue;
432         }
433 
434         if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
435 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
436             if (ngx_strcmp(&value[i].data[10], "n") == 0) {
437                 ls->ipv6only = 1;
438 
439             } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
440                 ls->ipv6only = 0;
441 
442             } else {
443                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
444                                    "invalid ipv6only flags \"%s\"",
445                                    &value[i].data[9]);
446                 return NGX_CONF_ERROR;
447             }
448 
449             ls->bind = 1;
450             continue;
451 #else
452             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
453                                "bind ipv6only is not supported "
454                                "on this platform");
455             return NGX_CONF_ERROR;
456 #endif
457         }
458 
459         if (ngx_strcmp(value[i].data, "ssl") == 0) {
460 #if (NGX_MAIL_SSL)
461             ngx_mail_ssl_conf_t  *sslcf;
462 
463             sslcf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_ssl_module);
464 
465             sslcf->listen = 1;
466             sslcf->file = cf->conf_file->file.name.data;
467             sslcf->line = cf->conf_file->line;
468 
469             ls->ssl = 1;
470 
471             continue;
472 #else
473             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
474                                "the \"ssl\" parameter requires "
475                                "ngx_mail_ssl_module");
476             return NGX_CONF_ERROR;
477 #endif
478         }
479 
480         if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
481 
482             if (ngx_strcmp(&value[i].data[13], "on") == 0) {
483                 ls->so_keepalive = 1;
484 
485             } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
486                 ls->so_keepalive = 2;
487 
488             } else {
489 
490 #if (NGX_HAVE_KEEPALIVE_TUNABLE)
491                 u_char     *p, *end;
492                 ngx_str_t   s;
493 
494                 end = value[i].data + value[i].len;
495                 s.data = value[i].data + 13;
496 
497                 p = ngx_strlchr(s.data, end, ':');
498                 if (p == NULL) {
499                     p = end;
500                 }
501 
502                 if (p > s.data) {
503                     s.len = p - s.data;
504 
505                     ls->tcp_keepidle = ngx_parse_time(&s, 1);
506                     if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
507                         goto invalid_so_keepalive;
508                     }
509                 }
510 
511                 s.data = (p < end) ? (p + 1) : end;
512 
513                 p = ngx_strlchr(s.data, end, ':');
514                 if (p == NULL) {
515                     p = end;
516                 }
517 
518                 if (p > s.data) {
519                     s.len = p - s.data;
520 
521                     ls->tcp_keepintvl = ngx_parse_time(&s, 1);
522                     if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
523                         goto invalid_so_keepalive;
524                     }
525                 }
526 
527                 s.data = (p < end) ? (p + 1) : end;
528 
529                 if (s.data < end) {
530                     s.len = end - s.data;
531 
532                     ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
533                     if (ls->tcp_keepcnt == NGX_ERROR) {
534                         goto invalid_so_keepalive;
535                     }
536                 }
537 
538                 if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
539                     && ls->tcp_keepcnt == 0)
540                 {
541                     goto invalid_so_keepalive;
542                 }
543 
544                 ls->so_keepalive = 1;
545 
546 #else
547 
548                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
549                                    "the \"so_keepalive\" parameter accepts "
550                                    "only \"on\" or \"off\" on this platform");
551                 return NGX_CONF_ERROR;
552 
553 #endif
554             }
555 
556             ls->bind = 1;
557 
558             continue;
559 
560 #if (NGX_HAVE_KEEPALIVE_TUNABLE)
561         invalid_so_keepalive:
562 
563             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
564                                "invalid so_keepalive value: \"%s\"",
565                                &value[i].data[13]);
566             return NGX_CONF_ERROR;
567 #endif
568         }
569 
570         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
571                            "the invalid \"%V\" parameter", &value[i]);
572         return NGX_CONF_ERROR;
573     }
574 
575     als = cmcf->listen.elts;
576 
577     for (n = 0; n < u.naddrs; n++) {
578         ls[n] = ls[0];
579 
580         ls[n].sockaddr = u.addrs[n].sockaddr;
581         ls[n].socklen = u.addrs[n].socklen;
582         ls[n].addr_text = u.addrs[n].name;
583         ls[n].wildcard = ngx_inet_wildcard(ls[n].sockaddr);
584 
585         for (i = 0; i < cmcf->listen.nelts - u.naddrs + n; i++) {
586 
587             if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen,
588                                  ls[n].sockaddr, ls[n].socklen, 1)
589                 != NGX_OK)
590             {
591                 continue;
592             }
593 
594             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
595                                "duplicate \"%V\" address and port pair",
596                                &ls[n].addr_text);
597             return NGX_CONF_ERROR;
598         }
599     }
600 
601     return NGX_CONF_OK;
602 }
603 
604 
605 static char *
ngx_mail_core_protocol(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)606 ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
607 {
608     ngx_mail_core_srv_conf_t  *cscf = conf;
609 
610     ngx_str_t          *value;
611     ngx_uint_t          m;
612     ngx_mail_module_t  *module;
613 
614     value = cf->args->elts;
615 
616     for (m = 0; cf->cycle->modules[m]; m++) {
617         if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {
618             continue;
619         }
620 
621         module = cf->cycle->modules[m]->ctx;
622 
623         if (module->protocol
624             && ngx_strcmp(module->protocol->name.data, value[1].data) == 0)
625         {
626             cscf->protocol = module->protocol;
627 
628             return NGX_CONF_OK;
629         }
630     }
631 
632     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
633                        "unknown protocol \"%V\"", &value[1]);
634     return NGX_CONF_ERROR;
635 }
636 
637 
638 static char *
ngx_mail_core_error_log(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)639 ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
640 {
641     ngx_mail_core_srv_conf_t  *cscf = conf;
642 
643     return ngx_log_set_log(cf, &cscf->error_log);
644 }
645 
646 
647 static char *
ngx_mail_core_resolver(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)648 ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
649 {
650     ngx_mail_core_srv_conf_t  *cscf = conf;
651 
652     ngx_str_t  *value;
653 
654     value = cf->args->elts;
655 
656     if (cscf->resolver != NGX_CONF_UNSET_PTR) {
657         return "is duplicate";
658     }
659 
660     if (ngx_strcmp(value[1].data, "off") == 0) {
661         cscf->resolver = NULL;
662         return NGX_CONF_OK;
663     }
664 
665     cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);
666     if (cscf->resolver == NULL) {
667         return NGX_CONF_ERROR;
668     }
669 
670     return NGX_CONF_OK;
671 }
672 
673 
674 char *
ngx_mail_capabilities(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)675 ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
676 {
677     char  *p = conf;
678 
679     ngx_str_t    *c, *value;
680     ngx_uint_t    i;
681     ngx_array_t  *a;
682 
683     a = (ngx_array_t *) (p + cmd->offset);
684 
685     value = cf->args->elts;
686 
687     for (i = 1; i < cf->args->nelts; i++) {
688         c = ngx_array_push(a);
689         if (c == NULL) {
690             return NGX_CONF_ERROR;
691         }
692 
693         *c = value[i];
694     }
695 
696     return NGX_CONF_OK;
697 }
698