diff options
author | Lennart Poettering <lennart@poettering.net> | 2005-11-20 15:54:52 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2005-11-20 15:54:52 +0000 |
commit | 8bb11c763e6a03d99be7215885c4d6440a74b7d7 (patch) | |
tree | 5ddaf087e355e075b0d30307061370dee9e2e697 | |
parent | 80c70e54132966b95bafb673b12b117bab632100 (diff) |
add initial (untouched) version from sebest
git-svn-id: file:///home/lennart/svn/public/mod_dnssd/trunk@3 634eccf8-0006-0410-930e-e16565b0b7de
-rw-r--r-- | Makefile.in | 3 | ||||
-rw-r--r-- | config.m4 | 39 | ||||
-rw-r--r-- | mod_zeroconf.c | 408 |
3 files changed, 450 insertions, 0 deletions
diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..167b343 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,3 @@ + +include $(top_srcdir)/build/special.mk + diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..280169f --- /dev/null +++ b/config.m4 @@ -0,0 +1,39 @@ +dnl modules enabled in this directory by default + +dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]]) + +APACHE_MODPATH_INIT(zeroconf) + +APR_ADDTO(LT_LDFLAGS,-export-dynamic) + +APACHE_MODULE(zeroconf, ZeroConf support, , , no, [ + AC_ARG_WITH(howl, APACHE_HELP_STRING(--with-howl=DIR,use a specific howl library), + [ + if test "x$withval" != "xyes" && test "x$withval" != "x"; then + ap_howl_base="$withval" + fi + ]) + if test "x$ap_howl_base" = "x"; then + AC_MSG_CHECKING([for howl location]) + AC_CACHE_VAL(ap_cv_howl,[ + for dir in /usr/ /usr/local/ ; do + if test -d $dir && test -f $dir/include/howl/howl.h; then + ap_cv_howl=$dir + break + fi + done + ]) + ap_howl_base=$ap_cv_howl + if test "x$ap_howl_base" = "x"; then + enable_howl=no + AC_MSG_RESULT([not found]) + else + AC_MSG_RESULT([$ap_howl_base]) + fi + fi + APR_ADDTO(INCLUDES, [-I${ap_howl_base}/include/howl]) + APR_ADDTO(LDFLAGS, [-L${ap_howl_base}/lib]) + APR_ADDTO(LIBS, [-lhowl -lpthread]) +]) + +APACHE_MODPATH_FINISH diff --git a/mod_zeroconf.c b/mod_zeroconf.c new file mode 100644 index 0000000..6246436 --- /dev/null +++ b/mod_zeroconf.c @@ -0,0 +1,408 @@ +/* + * $Id: mod_zeroconf.c,v 1.20 2004/10/07 15:30:25 sctemme Exp $ + * + * Copyright 2003, 2004 Sander Temme + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <sys/wait.h> + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "apr_strings.h" + +#include <howl.h> + +/* Macro to test result of Howl functions */ +#define TESTORBAIL(f) if ((f) != SW_OKAY) { \ + return HTTP_INTERNAL_SERVER_ERROR; \ + } + +typedef struct zc_cfg { + int enabled; + char *serviceName; + char *partialURI; +} + zc_cfg; + +sw_discovery howl_session; + +module AP_MODULE_DECLARE_DATA zeroconf_module; + +/* + * Locate server configuration record for specified server + */ +static zc_cfg *our_sconfig(const server_rec *s) +{ + return (zc_cfg *) ap_get_module_config(s->module_config, + &zeroconf_module); +} + +static const char *cmd_zeroconf_enable(cmd_parms *cmd, void *mconfig, + int enable) +{ + zc_cfg *cfg = our_sconfig(cmd->server); + cfg->enabled = enable; + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, cmd->server, + "Zeroconf switched %s", enable ? "on" : "off"); + + return NULL; +} + +static const char *cmd_zeroconf_register(cmd_parms *cmd, void *mconfig, + const char *serviceName, + const char *partialURI) +{ + zc_cfg *cfg = our_sconfig(cmd->server); + + cfg->serviceName = (char *) serviceName; + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, cmd->server, + "Service Name is \"%s\"", cfg->serviceName); + cfg->partialURI = (char *) partialURI; + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, cmd->server, + "Partial Path is %s", + (partialURI == NULL) ? "not set" : cfg->partialURI); + return NULL; +} + +static void *zc_create_server_config(apr_pool_t * p, server_rec *s) +{ + zc_cfg *cfg; + + cfg = (zc_cfg *) apr_palloc(p, sizeof(zc_cfg)); + cfg->enabled = 0; /* Default to disabled */ + cfg->serviceName = NULL; + cfg->partialURI = NULL; + + return (void *) cfg; +} + +static void *zc_merge_server_config(apr_pool_t * p, void *server1_conf, + void *server2_conf) +{ + zc_cfg *merged_cfg, *s1cfg, *s2cfg; + merged_cfg = (zc_cfg *) apr_palloc(p, sizeof(zc_cfg)); + s1cfg = (zc_cfg *) server1_conf; + s2cfg = (zc_cfg *) server2_conf; + + /* + * Inherit the zeroconf enablement. Config language does not allow + * turning it off, so just assume the main server value. + */ + merged_cfg->enabled = s1cfg->enabled; + /* + * Don't care about any main server registration info. + * Use vhost reg info, even if it's NULL. + */ + merged_cfg->serviceName = s2cfg->serviceName; + merged_cfg->partialURI = s2cfg->partialURI; + + return (void *) merged_cfg; +} + +static sw_result howl_publish_reply(sw_discovery rendezvous, + sw_discovery_publish_status status, + sw_discovery_oid id, + sw_opaque opaque) +{ + static sw_string status_text[] = { + "Started", + "Stopped", + "Name Collision", + "Invalid" + }; + fprintf(stderr, "publish reply: %s\n", status_text[status]); + return SW_OKAY; +} + +static sw_result host_publish_reply(sw_discovery rendezvous, + sw_discovery_publish_status status, + sw_discovery_oid id, + sw_opaque extra) +{ + static sw_string status_text[] = { + "Started", + "Stopped", + "Name Collision", + "Invalid" + }; + fprintf(stderr, "host registration reply: %s\n", status_text[status]); + fprintf(stderr, "Host registration callback: %d\n", status); + return SW_OKAY; +} + +/* + * Publish the hostname of server s through mDNS, using the IP + * address(es) that the server is bound to, if any. + * + */ + +static int zc_register_host(server_rec *s) +{ + + apr_sockaddr_t *mysock; + apr_status_t aprstatus; + char *thehostip; + sw_result hr; + sw_ipv4_address serveraddr; + sw_discovery_oid host_id; + + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, + s, "Registering host with name %s", + s->server_hostname); + for (mysock = s->addrs->host_addr; mysock; + mysock = mysock->next) { + /* + * First, get the IP address string from the server + * record + */ + aprstatus = apr_sockaddr_ip_get(&thehostip, mysock); + if (aprstatus != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, aprstatus, s, + "Failed to obtain server address"); + return HTTP_INTERNAL_SERVER_ERROR; + } + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, + 0, s, "Server bound to IP address [%s] port [%d][%d]", + thehostip, mysock->port,s->port); + /* + * Howl doesn't know what to do with IPv6 at this + * time + */ + if (mysock->family == APR_INET) { + if (mysock->sa.sin.sin_addr.s_addr != INADDR_ANY + && mysock->sa.sin.sin_addr.s_addr != INADDR_NONE) + /* What about localhost ? */ + { + hr = sw_ipv4_address_init_from_name(&serveraddr, thehostip); + } + else { + hr = sw_ipv4_address_init_from_this_host(&serveraddr); + } + } + hr = sw_discovery_publish_host(howl_session, + 0, + s->server_hostname, + NULL, + serveraddr, + host_publish_reply, + (sw_opaque) s, + &host_id); + if (hr != SW_OKAY) { + ap_log_error(APLOG_MARK, APLOG_ERR, hr, + s, + "Failed to publish hostname \"%s\" " + "with IP address \"%s\"", + s->server_hostname, thehostip); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + return OK; +} + +static int zc_post_config(apr_pool_t * pconf, apr_pool_t * plog, + apr_pool_t * ptemp, server_rec *s) +{ + void *data; + sw_discovery_oid *pubidPtr; + const char *userdata_key = "zeroconf_init_module"; + zc_cfg *cfg; + sw_result howl_result; + sw_discovery_oid howl_id; + sw_port serverport; + sw_text_record text_record; + sw_octets pathinfo; + sw_ulong pilength; + char *thehostname; +#if APR_HAS_FORK + apr_proc_t *callbackchild; + apr_status_t forkstatus; +#endif + server_rec *ws; /* Walk Server; we need the top server_rec + * later */ + + /* raise(SIGSTOP); */ + + apr_pool_userdata_get(&data, userdata_key, s->process->pool); + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s, + "In post_config, userdata pointer is %#lx, pid is %d", + data, getpid()); + if (!data) { + /* + * Tell the world we've been here before, so we don't end up forking + * more than one callback processes. + */ + apr_pool_userdata_set((const void *) 1, userdata_key, + apr_pool_cleanup_null, s->process->pool); + } else { + /* Check if Zeroconf has been enabled globally. If not, bail here */ + cfg = our_sconfig(s); + if (cfg->enabled == 0) { + return OK; + } + /* Still here? Let's go. */ + TESTORBAIL(sw_discovery_init(&howl_session)); + + /* + * Need to know the hostname of the machine, so we know not to + * re-register an existing one + */ + thehostname = apr_palloc(pconf, APRMAXHOSTLEN + 1); + if (!thehostname) { + return HTTP_INTERNAL_SERVER_ERROR; + } + if (!APR_STATUS_IS_SUCCESS(apr_gethostname(thehostname, + APRMAXHOSTLEN, pconf))) { + return HTTP_INTERNAL_SERVER_ERROR; + } + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s, + "The hostname is [%s]", thehostname); + for (ws = s; ws; ws = ws->next) { + cfg = our_sconfig(ws); + if (cfg->serviceName) { + if (cfg->partialURI) { + TESTORBAIL(sw_text_record_init(&text_record)); + TESTORBAIL(sw_text_record_add_key_and_string_value(text_record, "path", + cfg->partialURI)); + pathinfo = sw_text_record_bytes(text_record); + pilength = sw_text_record_len(text_record); + } + else { + pathinfo = NULL; + pilength = 0; + } + /* + * for main server port is fetched from eg: + * ServerName hostnam:port + * + * for virtualhost eg: + * <VirtualHost ip:port> + */ + serverport = ws->is_virtual ? ws->addrs->host_port: ws->port; + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, ws, + "Publishing service with service name [%s]; " + "hostname [%s]; port [%d], path [%s]", + cfg->serviceName, + ws->server_hostname, serverport, cfg->partialURI); + if (apr_strnatcasecmp(thehostname, ws->server_hostname) != 0) { + /* Shouldn't throw away the result here. What's up with + * that? + */ + zc_register_host(ws); + } + howl_result = sw_discovery_publish(howl_session, + 0, + cfg->serviceName, + "_http._tcp", + NULL, + ws->server_hostname, + serverport, + pathinfo, + pilength, + howl_publish_reply, + (sw_opaque) ws, + &howl_id); + if (howl_result != SW_OKAY) { + ap_log_error(APLOG_MARK, APLOG_ERR, howl_result, + ws, "Failed to publish service %s", + cfg->serviceName); + return HTTP_INTERNAL_SERVER_ERROR; + } + pubidPtr = apr_palloc(s->process->pool, + sizeof(sw_discovery_oid)); + if (!pubidPtr) { + return HTTP_INTERNAL_SERVER_ERROR; + } + /* + * Store publish ID in the server's pool userdata, using the + * serviceName as key. The serviceName should be unique, right? + */ + *pubidPtr = howl_id; + apr_pool_userdata_set(pubidPtr, cfg->serviceName, + apr_pool_cleanup_null, + ws->process->pool); + } /* serviceName set for server config */ + } /* Server walk */ + + /* + * On unix-like systems, we need to fork a child process to + * run the callback function. On Windows, this is not necessary. + */ +#if APR_HAS_FORK + callbackchild = apr_palloc(s->process->pool, sizeof(apr_proc_t)); + if (callbackchild == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Allocating process struct memory from pool failed"); + return HTTP_INTERNAL_SERVER_ERROR; + } + switch(forkstatus = apr_proc_fork(callbackchild, s->process->pool)) { + case APR_INCHILD: + /* I'm the child */ + sw_discovery_run(howl_session); + /* + * This function never returns ... + */ + break; /* Not reached */ + case APR_INPARENT: + /* I'm the parent */ + apr_pool_note_subprocess(s->process->pool, callbackchild, + APR_KILL_AFTER_TIMEOUT); + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s, + "Forked callback child process with pid [%d]", + callbackchild->pid); + break; + default: + /* FUBAR: log and bail */ + ap_log_error(APLOG_MARK, APLOG_ERR, forkstatus, s, + "Failed to fork callback child"); + return HTTP_INTERNAL_SERVER_ERROR; + } +#endif /* APR_HAS_FORK */ + } /* If first time run */ + return OK; +} + +static void zc_register_hooks(apr_pool_t * p) +{ + ap_hook_post_config(zc_post_config, NULL, NULL, APR_HOOK_LAST); +} + +static const command_rec zc_cmds[] = { + AP_INIT_FLAG("Zeroconf", + cmd_zeroconf_enable, + NULL, + GLOBAL_ONLY, + "Enable/disable Zeroconf registration"), + AP_INIT_TAKE12("ZeroconfRegister", + cmd_zeroconf_register, + NULL, + NOT_IN_DIR_LOC_FILE, + "Register server or virtual host with Zeroconf mDNS responder.\n" + "Arguments are service name and, optionally, partial URI"), + {NULL} +}; + +module AP_MODULE_DECLARE_DATA zeroconf_module = { + STANDARD20_MODULE_STUFF, + NULL, + NULL, + zc_create_server_config, + zc_merge_server_config, + zc_cmds, + zc_register_hooks, +}; |