Hi.
I'm trying to implement UDP service (TFTP, etc) as an Apache module,
and would like to know official (= future compatible) way to
implement it.
After looking into 2.2.4-dev source, I came up with two different
implementations. Both works to some degree, with different drawbacks.
Type 1 implementation hooks into two very early stages to do it:
"create_server_config" and "command handler". By hooking in
early enough, I can assure UDP socket to exist in to-be-closed
"old_listeners", and revive it back to "ap_listeners" by calling
"ap_set_listener" explicitly (which is a handler for "Listen"
directive). This way, I can make UDP and TCP with the same port
number to coexist.
However, there's a drawback. In error_log file, I see lot's of
(111)Connection refused: connect to listener on 0.0.0.0:<udpport>
errors generated by "dummy_connection" (in mpm_common.c). Also, code
isn't clean enough because some key structures aren't ready yet at
this stage - I need to use static variables so I can do some fixup
work at later stage.
Type 2 implementation deals with the above by using "Listen <udpport>"
as a helper directive. By letting "Listen <udpport>" allocate a slot
in "ap_listeners" early enough, UDP initialization can be delayed
back to "open_log" stage.
But as a drawback, I end up with unwanted extra HTTP service running
on TCP port with the same port number as UDP.
For complete module implementation, I still need to find out how
can I do bucket handling, logging, etc. But before going further,
I decided it'd be safer to ask for a recommended way (or at least
a hint for it).
Can anyone advise me on this?
Thanks in advance.
--
Taisuke Yamada <[EMAIL PROTECTED]>, http://rakugaki.org/
2268 E9A2 D4F9 014E F11D 1DF7 DCA3 83BC 78E5 CD3A
Message to my public address may not be handled in a timely manner.
For a direct contact, please use my private address on my namecard.
/**
* Experimental UDP handling module for Apache 2.2
* This version relies on built-in "Listen" directive.
*
* @version 0.02
* @author Taisuke Yamada
*/
#include "ap_listen.h"
#include "http_log.h"
#include "apr_buckets.h"
#include "util_filter.h"
module AP_MODULE_DECLARE_DATA udp_echo_module;
typedef struct {
int port;
} udp_echo_config;
/**
* "echo" UDP protocol handler
*/
static apr_status_t
udp_accept(void **csd, ap_listen_rec *lr, apr_pool_t *p) {
char *buf;
apr_size_t len = APR_BUCKET_BUFF_SIZE;
buf = apr_pcalloc(p, len);
apr_socket_recvfrom(lr->bind_addr, lr->sd, 0, buf, &len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "recv: %d", len);
apr_socket_sendto(lr->sd, lr->bind_addr, 0, buf, &len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "send: %d", len);
// Avoid bucket handling by returning an error.
// It's not that bucket handling code fails with UDP (haven't tried yet)
// - it's just I wanted simplicity for faster feasibility test.
// I guess APR_SUCCESS or APR_EGENERAL is what's really expected.
return APR_EOF;
}
/**
* Simpler version of "alloc_listener" in listen.c.
* I have to do it myself as built-in version always allocates
* a socket as SOCK_STREAM socket.
*/
static ap_listen_rec *
alloc_listener_stub(apr_pool_t *p, int port) {
ap_listen_rec *new;
apr_status_t rc;
apr_sockaddr_t *sa;
apr_sockaddr_info_get(&sa, NULL, APR_INET, port, 0, p);
new = apr_palloc(p, sizeof(ap_listen_rec));
new->accept_func = udp_accept;
new->active = 1;
new->next = NULL;
new->bind_addr = sa; /* dummy */
new->protocol = (const char *)apr_pstrdup(p, "udp");
return new;
}
/**
* Prepare socket as UDP socket. Error handling removed for clarity.
*/
static apr_status_t
config_socket(apr_pool_t *p, ap_listen_rec *lr, int port) {
apr_sockaddr_t *sa;
int one = 1;
apr_sockaddr_info_get(&sa, NULL, APR_INET, port, 0, p);
lr->bind_addr = sa;
apr_socket_create(&lr->sd, lr->bind_addr->family, SOCK_DGRAM, 0, p);
apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1);
apr_socket_bind(lr->sd, lr->bind_addr);
apr_socket_opt_set(lr->sd, APR_SO_REUSEADDR, one);
return APR_SUCCESS;
}
static void *
create_server_config(apr_pool_t *p, server_rec *s) {
udp_echo_config *config = apr_pcalloc(p, sizeof *config);
config->port = 0;
return config;
}
/**
* Handler for "UDPEchoPort <port>" directive.
*/
static const char *
config_port(cmd_parms *cmd, void *dummy, const char *value) {
udp_echo_config *config =
ap_get_module_config(cmd->server->module_config, &udp_echo_module);
config->port = atoi(value);
return NULL;
}
static int
open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
udp_echo_config *config =
ap_get_module_config(s->module_config, &udp_echo_module);
ap_listen_rec *lr, *last;
if (config->port <= 0) return OK;
// setup UDP port
lr = alloc_listener_stub(s->process->pool, config->port);
config_socket(s->process->pool, lr, config->port);
// This code won't work unless "Listen <udpport>" is also set.
// By having helper "Listen <udpport>", socket created here
// will revive from "old_listeners" when "ap_setup_listeners" is
// called from MPM.
//
// As a sideeffect of using "Listen" directive, TCP port with
// the same port number will be opened for HTTP service.
// I don't like it, but it seems this is needed by "dummy_connection"
// in mpm_common.c.
for (last = ap_listeners; last && last->next; last = last->next);
if (last == NULL) { ap_listeners = lr; } else { last->next = lr; }
return OK;
}
static void
register_hooks(apr_pool_t *p) {
ap_hook_open_logs(open_logs, NULL, NULL, APR_HOOK_MIDDLE);
}
static const command_rec
commands[] = {
AP_INIT_TAKE1("UDPEchoPort", config_port, NULL, RSRC_CONF,
"Run an UDP echo server on this port"),
{ NULL }
};
module AP_MODULE_DECLARE_DATA udp_echo_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
create_server_config, /* create per-server config structure */
NULL, /* merge per-server config structures */
commands, /* command apr_table_t */
register_hooks /* register hooks */
};
/**
* Experimental UDP handling module for Apache 2.2
*
* @version 0.01
* @author Taisuke Yamada
*/
#include "ap_listen.h"
#include "http_log.h"
#include "apr_buckets.h"
#include "util_filter.h"
module AP_MODULE_DECLARE_DATA udp_echo_module;
typedef struct {
int port;
} udp_echo_config;
static process_rec *GP;
static ap_listen_rec *LR;
/**
* "echo" UDP protocol handler
*/
static apr_status_t
udp_accept(void **csd, ap_listen_rec *lr, apr_pool_t *p) {
char *buf;
apr_size_t len = APR_BUCKET_BUFF_SIZE;
buf = apr_pcalloc(p, len);
apr_socket_recvfrom(lr->bind_addr, lr->sd, 0, buf, &len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "recv: %d", len);
apr_socket_sendto(lr->sd, lr->bind_addr, 0, buf, &len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "send: %d", len);
// Avoid bucket handling by returning an error.
// It's not that bucket handling code fails with UDP (haven't tried yet)
// - it's just I wanted simplicity for faster feasibility test.
// I guess APR_SUCCESS or APR_EGENERAL is what's really expected.
return APR_EOF;
}
/**
* Simpler version of "alloc_listener" in listen.c.
* I have to do it myself as built-in version always allocates
* a socket as SOCK_STREAM socket.
*/
static ap_listen_rec *
alloc_listener_stub(apr_pool_t *p, int port) {
ap_listen_rec *new;
apr_status_t rc;
apr_sockaddr_t *sa;
apr_sockaddr_info_get(&sa, NULL, APR_INET, port, 0, p);
new = apr_palloc(p, sizeof(ap_listen_rec));
new->accept_func = udp_accept;
new->active = 1;
new->next = NULL;
new->bind_addr = sa; /* dummy */
new->protocol = (const char *)apr_pstrdup(p, "udp");
return new;
}
/**
* Prepare UDP socket. Error handling removed for clarity.
*/
static apr_status_t
config_socket(apr_pool_t *p, ap_listen_rec *lr, int port) {
apr_sockaddr_t *sa;
int one = 1;
apr_sockaddr_info_get(&sa, NULL, APR_INET, port, 0, p);
lr->bind_addr = sa;
apr_socket_create(&lr->sd, lr->bind_addr->family, SOCK_DGRAM, 0, p);
apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1);
apr_socket_bind(lr->sd, lr->bind_addr);
apr_socket_opt_set(lr->sd, APR_SO_REUSEADDR, one);
return APR_SUCCESS;
}
static void *
create_server_config(apr_pool_t *p, server_rec *s) {
ap_listen_rec *last;
udp_echo_config *config = apr_pcalloc(p, sizeof *config);
// Push UDP socket into "ap_listeners".
// By pushing it before "ap_listen_pre_config" call by MPM,
// I can assure this socket to be in "old_listeners" by the time
// I reach "config_port" command handler.
GP = s->process;
LR = alloc_listener_stub(GP->pool, 0);
for (last = ap_listeners; last && last->next; last = last->next);
if (last == NULL) { ap_listeners = LR; } else { last->next = LR; }
config->port = 0;
return config;
}
/**
* Handler for "UDPEchoPort <port>" directive.
*/
static const char *
config_port(cmd_parms *cmd, void *dummy, const char *value) {
char *args[] = { (char *)value };
udp_echo_config *config =
ap_get_module_config(cmd->server->module_config, &udp_echo_module);
// Now the port is known, I can update stub "ap_listen_rec" entry
// which should now be in "old_listeners".
config->port = atoi(value);
config_socket(GP->pool, LR, config->port);
// ...and by this call, I can revive UDP socket in "old_listeners"
// back to "ap_listeners" without creating TCP socket at the same port.
ap_set_listener(cmd, dummy, 1, args);
return NULL;
}
static const command_rec
commands[] = {
AP_INIT_TAKE1("UDPEchoPort", config_port, NULL, RSRC_CONF,
"Run an UDP echo server on this port"),
{ NULL }
};
module AP_MODULE_DECLARE_DATA udp_echo_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
create_server_config, /* create per-server config structure */
NULL, /* merge per-server config structures */
commands, /* command apr_table_t */
NULL /* register hooks */
};