/* * Copyright (c) 2012 Roman Arutyunyan */ #include #include #include #include "ngx_rtmp.h" static void *ngx_rtmp_core_create_main_conf(ngx_conf_t *cf); static void *ngx_rtmp_core_create_srv_conf(ngx_conf_t *cf); static char *ngx_rtmp_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); static char *ngx_rtmp_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_rtmp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_conf_deprecated_t ngx_conf_deprecated_so_keepalive = { ngx_conf_deprecated, "so_keepalive", "so_keepalive\" parameter of the \"listen" }; static ngx_command_t ngx_rtmp_core_commands[] = { { ngx_string("server"), NGX_RTMP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_rtmp_core_server, 0, 0, NULL }, { ngx_string("listen"), NGX_RTMP_SRV_CONF|NGX_CONF_TAKE12, ngx_rtmp_core_listen, NGX_RTMP_SRV_CONF_OFFSET, 0, NULL }, { ngx_string("so_keepalive"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_RTMP_SRV_CONF_OFFSET, offsetof(ngx_rtmp_core_srv_conf_t, so_keepalive), &ngx_conf_deprecated_so_keepalive }, { ngx_string("timeout"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, NGX_RTMP_SRV_CONF_OFFSET, offsetof(ngx_rtmp_core_srv_conf_t, timeout), NULL }, { ngx_string("max_streams"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_RTMP_SRV_CONF_OFFSET, offsetof(ngx_rtmp_core_srv_conf_t, max_streams), NULL }, { ngx_string("ack_window"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_RTMP_SRV_CONF_OFFSET, offsetof(ngx_rtmp_core_srv_conf_t, ack_window), NULL }, { ngx_string("chunk_size"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_RTMP_SRV_CONF_OFFSET, offsetof(ngx_rtmp_core_srv_conf_t, chunk_size), NULL }, { ngx_string("max_buf"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_RTMP_SRV_CONF_OFFSET, offsetof(ngx_rtmp_core_srv_conf_t, max_buf), NULL }, { ngx_string("wait_key_frame"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_flag_slot, NGX_RTMP_SRV_CONF_OFFSET, offsetof(ngx_rtmp_core_srv_conf_t, wait_key_frame), NULL }, ngx_null_command }; static ngx_rtmp_module_t ngx_rtmp_core_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ ngx_rtmp_core_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ ngx_rtmp_core_create_srv_conf, /* create server configuration */ ngx_rtmp_core_merge_srv_conf /* merge server configuration */ }; ngx_module_t ngx_rtmp_core_module = { NGX_MODULE_V1, &ngx_rtmp_core_module_ctx, /* module context */ ngx_rtmp_core_commands, /* module directives */ NGX_RTMP_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_rtmp_core_create_main_conf(ngx_conf_t *cf) { ngx_rtmp_core_main_conf_t *cmcf; cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_core_main_conf_t)); if (cmcf == NULL) { return NULL; } if (ngx_array_init(&cmcf->servers, cf->pool, 4, sizeof(ngx_rtmp_core_srv_conf_t *)) != NGX_OK) { return NULL; } if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_rtmp_listen_t)) != NGX_OK) { return NULL; } return cmcf; } static void * ngx_rtmp_core_create_srv_conf(ngx_conf_t *cf) { ngx_rtmp_core_srv_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_core_srv_conf_t)); if (conf == NULL) { return NULL; } conf->timeout = NGX_CONF_UNSET_MSEC; conf->so_keepalive = NGX_CONF_UNSET; conf->max_streams = NGX_CONF_UNSET; conf->chunk_size = NGX_CONF_UNSET; conf->ack_window = NGX_CONF_UNSET; conf->max_buf = NGX_CONF_UNSET; conf->wait_key_frame = NGX_CONF_UNSET; return conf; } static char * ngx_rtmp_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_rtmp_core_srv_conf_t *prev = parent; ngx_rtmp_core_srv_conf_t *conf = child; ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0); ngx_conf_merge_value(conf->max_streams, prev->max_streams, 16); ngx_conf_merge_value(conf->chunk_size, prev->chunk_size, 4096); ngx_conf_merge_uint_value(conf->ack_window, prev->ack_window, 5000000); ngx_conf_merge_size_value(conf->max_buf, prev->max_buf, 128 * 1024); ngx_conf_merge_value(conf->wait_key_frame, prev->wait_key_frame, 1); if (prev->pool == NULL) { prev->pool = ngx_create_pool(8192, cf->log); if (prev->pool == NULL) { return NGX_CONF_ERROR; } } conf->pool = prev->pool; return NGX_CONF_OK; } static char * ngx_rtmp_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_rtmp_module_t *module; ngx_rtmp_conf_ctx_t *ctx, *rtmp_ctx; ngx_rtmp_core_srv_conf_t *cscf, **cscfp; ngx_rtmp_core_main_conf_t *cmcf; ctx = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_conf_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; } rtmp_ctx = cf->ctx; ctx->main_conf = rtmp_ctx->main_conf; /* the server{}'s srv_conf */ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_rtmp_max_module); if (ctx->srv_conf == NULL) { return NGX_CONF_ERROR; } for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_RTMP_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_rtmp_core_module.ctx_index]; cscf->ctx = ctx; cmcf = ctx->main_conf[ngx_rtmp_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_RTMP_SRV_CONF; rv = ngx_conf_parse(cf, NULL); *cf = pcf; return rv; } static char * ngx_rtmp_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, m; struct sockaddr *sa; ngx_rtmp_listen_t *ls; ngx_rtmp_module_t *module; struct sockaddr_in *sin; ngx_rtmp_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_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_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 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_rtmp_listen_t)); ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen); ls->socklen = u.socklen; ls->wildcard = u.wildcard; ls->ctx = cf->ctx; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_RTMP_MODULE) { continue; } module = ngx_modules[m]->ctx; } 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 = 2; } 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, 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_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; }