diff src/stream/ngx_stream_core_module.c @ 6115:61d7ae76647d

Stream: port from NGINX+.
author Ruslan Ermilov <ru@nginx.com>
date Mon, 20 Apr 2015 13:05:11 +0300
parents
children 4f6efabcb09b
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/stream/ngx_stream_core_module.c
@@ -0,0 +1,495 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+static void *ngx_stream_core_create_main_conf(ngx_conf_t *cf);
+static void *ngx_stream_core_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static char *ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
+
+static ngx_command_t  ngx_stream_core_commands[] = {
+
+    { ngx_string("server"),
+      NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+      ngx_stream_core_server,
+      0,
+      0,
+      NULL },
+
+    { ngx_string("listen"),
+      NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+      ngx_stream_core_listen,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("error_log"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+      ngx_stream_core_error_log,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_stream_module_t  ngx_stream_core_module_ctx = {
+    ngx_stream_core_create_main_conf,      /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    ngx_stream_core_create_srv_conf,       /* create server configuration */
+    ngx_stream_core_merge_srv_conf         /* merge server configuration */
+};
+
+
+ngx_module_t  ngx_stream_core_module = {
+    NGX_MODULE_V1,
+    &ngx_stream_core_module_ctx,           /* module context */
+    ngx_stream_core_commands,              /* module directives */
+    NGX_STREAM_MODULE,                     /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_stream_core_create_main_conf(ngx_conf_t *cf)
+{
+    ngx_stream_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_main_conf_t));
+    if (cmcf == NULL) {
+        return NULL;
+    }
+
+    if (ngx_array_init(&cmcf->servers, cf->pool, 4,
+                       sizeof(ngx_stream_core_srv_conf_t *))
+        != NGX_OK)
+    {
+        return NULL;
+    }
+
+    if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_stream_listen_t))
+        != NGX_OK)
+    {
+        return NULL;
+    }
+
+    return cmcf;
+}
+
+
+static void *
+ngx_stream_core_create_srv_conf(ngx_conf_t *cf)
+{
+    ngx_stream_core_srv_conf_t  *cscf;
+
+    cscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_srv_conf_t));
+    if (cscf == NULL) {
+        return NULL;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     cscf->handler = NULL;
+     *     cscf->error_log = NULL;
+     */
+
+    cscf->file_name = cf->conf_file->file.name.data;
+    cscf->line = cf->conf_file->line;
+
+    return cscf;
+}
+
+
+static char *
+ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_stream_core_srv_conf_t *prev = parent;
+    ngx_stream_core_srv_conf_t *conf = child;
+
+    if (conf->handler == NULL) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                      "no handler for server in %s:%ui",
+                      conf->file_name, conf->line);
+        return NGX_CONF_ERROR;
+    }
+
+    if (conf->error_log == NULL) {
+        if (prev->error_log) {
+            conf->error_log = prev->error_log;
+        } else {
+            conf->error_log = &cf->cycle->new_log;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_stream_core_srv_conf_t  *cscf = conf;
+
+    return ngx_log_set_log(cf, &cscf->error_log);
+}
+
+
+static char *
+ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    char                         *rv;
+    void                         *mconf;
+    ngx_uint_t                    m;
+    ngx_conf_t                    pcf;
+    ngx_stream_module_t          *module;
+    ngx_stream_conf_ctx_t        *ctx, *stream_ctx;
+    ngx_stream_core_srv_conf_t   *cscf, **cscfp;
+    ngx_stream_core_main_conf_t  *cmcf;
+
+    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));
+    if (ctx == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    stream_ctx = cf->ctx;
+    ctx->main_conf = stream_ctx->main_conf;
+
+    /* the server{}'s srv_conf */
+
+    ctx->srv_conf = ngx_pcalloc(cf->pool,
+                                sizeof(void *) * ngx_stream_max_module);
+    if (ctx->srv_conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    for (m = 0; ngx_modules[m]; m++) {
+        if (ngx_modules[m]->type != NGX_STREAM_MODULE) {
+            continue;
+        }
+
+        module = ngx_modules[m]->ctx;
+
+        if (module->create_srv_conf) {
+            mconf = module->create_srv_conf(cf);
+            if (mconf == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
+        }
+    }
+
+    /* the server configuration context */
+
+    cscf = ctx->srv_conf[ngx_stream_core_module.ctx_index];
+    cscf->ctx = ctx;
+
+    cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index];
+
+    cscfp = ngx_array_push(&cmcf->servers);
+    if (cscfp == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    *cscfp = cscf;
+
+
+    /* parse inside server{} */
+
+    pcf = *cf;
+    cf->ctx = ctx;
+    cf->cmd_type = NGX_STREAM_SRV_CONF;
+
+    rv = ngx_conf_parse(cf, NULL);
+
+    *cf = pcf;
+
+    return rv;
+}
+
+
+static char *
+ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    size_t                        len, off;
+    in_port_t                     port;
+    ngx_str_t                    *value;
+    ngx_url_t                     u;
+    ngx_uint_t                    i;
+    struct sockaddr              *sa;
+    struct sockaddr_in           *sin;
+    ngx_stream_listen_t          *ls;
+    ngx_stream_core_main_conf_t  *cmcf;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6          *sin6;
+#endif
+
+    value = cf->args->elts;
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+
+    u.url = value[1];
+    u.listen = 1;
+
+    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+        if (u.err) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "%s in \"%V\" of the \"listen\" directive",
+                               u.err, &u.url);
+        }
+
+        return NGX_CONF_ERROR;
+    }
+
+    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
+
+    ls = cmcf->listen.elts;
+
+    for (i = 0; i < cmcf->listen.nelts; i++) {
+
+        sa = (struct sockaddr *) ls[i].sockaddr;
+
+        if (sa->sa_family != u.family) {
+            continue;
+        }
+
+        switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            off = offsetof(struct sockaddr_in6, sin6_addr);
+            len = 16;
+            sin6 = (struct sockaddr_in6 *) sa;
+            port = sin6->sin6_port;
+            break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+        case AF_UNIX:
+            off = offsetof(struct sockaddr_un, sun_path);
+            len = sizeof(((struct sockaddr_un *) sa)->sun_path);
+            port = 0;
+            break;
+#endif
+
+        default: /* AF_INET */
+            off = offsetof(struct sockaddr_in, sin_addr);
+            len = 4;
+            sin = (struct sockaddr_in *) sa;
+            port = sin->sin_port;
+            break;
+        }
+
+        if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) {
+            continue;
+        }
+
+        if (port != u.port) {
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "duplicate \"%V\" address and port pair", &u.url);
+        return NGX_CONF_ERROR;
+    }
+
+    ls = ngx_array_push(&cmcf->listen);
+    if (ls == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ngx_memzero(ls, sizeof(ngx_stream_listen_t));
+
+    ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);
+
+    ls->socklen = u.socklen;
+    ls->wildcard = u.wildcard;
+    ls->ctx = cf->ctx;
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+    ls->ipv6only = 1;
+#endif
+
+    for (i = 2; i < cf->args->nelts; i++) {
+
+        if (ngx_strcmp(value[i].data, "bind") == 0) {
+            ls->bind = 1;
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+            struct sockaddr  *sa;
+            u_char            buf[NGX_SOCKADDR_STRLEN];
+
+            sa = (struct sockaddr *) ls->sockaddr;
+
+            if (sa->sa_family == AF_INET6) {
+
+                if (ngx_strcmp(&value[i].data[10], "n") == 0) {
+                    ls->ipv6only = 1;
+
+                } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
+                    ls->ipv6only = 0;
+
+                } else {
+                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                       "invalid ipv6only flags \"%s\"",
+                                       &value[i].data[9]);
+                    return NGX_CONF_ERROR;
+                }
+
+                ls->bind = 1;
+
+            } else {
+                len = ngx_sock_ntop(sa, ls->socklen, buf,
+                                    NGX_SOCKADDR_STRLEN, 1);
+
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "ipv6only is not supported "
+                                   "on addr \"%*s\", ignored", len, buf);
+            }
+
+            continue;
+#else
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "bind ipv6only is not supported "
+                               "on this platform");
+            return NGX_CONF_ERROR;
+#endif
+        }
+
+        if (ngx_strcmp(value[i].data, "ssl") == 0) {
+#if (NGX_STREAM_SSL)
+            ls->ssl = 1;
+            continue;
+#else
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "the \"ssl\" parameter requires "
+                               "ngx_stream_ssl_module");
+            return NGX_CONF_ERROR;
+#endif
+        }
+
+        if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
+
+            if (ngx_strcmp(&value[i].data[13], "on") == 0) {
+                ls->so_keepalive = 1;
+
+            } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
+                ls->so_keepalive = 2;
+
+            } else {
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+                u_char     *p, *end;
+                ngx_str_t   s;
+
+                end = value[i].data + value[i].len;
+                s.data = value[i].data + 13;
+
+                p = ngx_strlchr(s.data, end, ':');
+                if (p == NULL) {
+                    p = end;
+                }
+
+                if (p > s.data) {
+                    s.len = p - s.data;
+
+                    ls->tcp_keepidle = ngx_parse_time(&s, 1);
+                    if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                s.data = (p < end) ? (p + 1) : end;
+
+                p = ngx_strlchr(s.data, end, ':');
+                if (p == NULL) {
+                    p = end;
+                }
+
+                if (p > s.data) {
+                    s.len = p - s.data;
+
+                    ls->tcp_keepintvl = ngx_parse_time(&s, 1);
+                    if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                s.data = (p < end) ? (p + 1) : end;
+
+                if (s.data < end) {
+                    s.len = end - s.data;
+
+                    ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
+                    if (ls->tcp_keepcnt == NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
+                    && ls->tcp_keepcnt == 0)
+                {
+                    goto invalid_so_keepalive;
+                }
+
+                ls->so_keepalive = 1;
+
+#else
+
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "the \"so_keepalive\" parameter accepts "
+                                   "only \"on\" or \"off\" on this platform");
+                return NGX_CONF_ERROR;
+
+#endif
+            }
+
+            ls->bind = 1;
+
+            continue;
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+        invalid_so_keepalive:
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid so_keepalive value: \"%s\"",
+                               &value[i].data[13]);
+            return NGX_CONF_ERROR;
+#endif
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "the invalid \"%V\" parameter", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}