After taking my own advice on Sunday, I reworked everything I'd done for
UDP support and focused on allowing the core I/O filters to work with
non-connected sockets.  I'm happy to report that I've come up with a
working prototype for prefork that fully supports UDP and doesn't
require substantial amounts of mucking with the MPM code (so it will
hopefully be fairly simple to patch the other MPMs).  It requires
patches to APR (to add a apr_socket_sendtov function, needed by the core
output filter) and APR-Util to support apr_socket_readfrom in socket
buckets that aren't connected).  Both of these patches would be useful
in APR/APR-Util regardles (although the APR patch still requires work
for win32, beos and netware - I suppose I'll tackle at least win32
[which I'm familiar with] later today and we'll see about the others).
As such, I've added these both to bugzilla to
https://issues.apache.org/bugzilla/show_bug.cgi?id=43309 and
https://issues.apache.org/bugzilla/show_bug.cgi?id=43302, respectively
(thus the CC to [EMAIL PROTECTED]).

To go through what I've done in the main patch (for those who want an
idea of what they're looking at):

* Export ap_alloc_listner and take socket type/protocol as arguments
* In core_create_conn, if the remote_addr of the socket can't be
detected from the socket, try to run recvfrom in PEEK mode to get the
peer address
* Core input filters run as-is (patch to apr-util does everything)
* Core output filter tries (in init) to see if socket is connected.  If
not, it turns of a new flag, connected, in ctx which is ultimately used
to decide whether to pass data to apr_socket_sendv or apr_socket_sendtov
* Additionally, disable SENDFILE for non-stream sockets
* Create ListenUDP config directive to create UDP listeners
* Alter all MPMs to only call lr->accept_func if it's defined
* In prefork, re-create the pollset in every iteration of child_main and
include that in the mutex.  Hack the use of lr->active to add a flag
(value 2) for in-use UDP sockets (and don't add them to the pollset if
they're found).  Don't connect UDP sockets and don't close them after
the connection is finished - just re-allow the socket to be polled
* Don't try to disable nagle on non-TCP sockets
* Support non-TCP dummy connections

I already have a prototype version of a DNS protocol module which has
worked for months over TCP and seems so far to work flawlessly using
this patch.  Is there any interest in adopting this to the httpd
project?  If so, I'd love to continue helping out on this.

  Issac
Index: buckets/apr_buckets_socket.c
===================================================================
--- buckets/apr_buckets_socket.c        (revision 572605)
+++ buckets/apr_buckets_socket.c        (working copy)
@@ -23,6 +23,8 @@
     char *buf;
     apr_status_t rv;
     apr_interval_time_t timeout;
+    apr_sockaddr_t *peerptr, peer;
+    int sd_type;
 
     if (block == APR_NONBLOCK_READ) {
         apr_socket_timeout_get(p, &timeout);
@@ -33,7 +35,18 @@
     *len = APR_BUCKET_BUFF_SIZE;
     buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */
 
-    rv = apr_socket_recv(p, buf, len);
+    apr_socket_type_get(p, &sd_type);
+    if (sd_type == SOCK_STREAM) {
+        rv = apr_socket_recv(p, buf, len);    
+    } else {
+        /* Is socket connected? */
+        if (apr_socket_addr_get(&peerptr, APR_REMOTE, p) != APR_SUCCESS) {
+            rv = apr_socket_recv(p, buf, len);    
+        } else {
+            /* Caller is responsible for detecting peer on his own if needed */
+            rv = apr_socket_recvfrom(&peer, p, 0, buf, len);
+        }
+    }
 
     if (block == APR_NONBLOCK_READ) {
         apr_socket_timeout_set(p, timeout);
Index: include/ap_listen.h
===================================================================
--- include/ap_listen.h (revision 565699)
+++ include/ap_listen.h (working copy)
@@ -81,6 +81,26 @@
 AP_DECLARE(void) ap_listen_pre_config(void);
 
 /**
+ * Allocate a new listener to be created during ap_setup_listeners.
+ * @param process The process_rec of the parent process
+ * @param addr The IP address to bind the socket to (uses same format as
+ * the Listen directive in httpd.conf)
+ * @param port The port to bind the socket to
+ * @param type The socket type (SOCK_STREAM, etc)
+ * @param protocol The socket protocol (APR_PROTO_TCP, etc)
+ * @param proto The optional protocol argument is not required for most
+ * configurations. If not specified, https is the default for port 443
+ * and http the default for all other ports. The protocol is used to
+ * determine which module should handle a request, and to apply
+ * protocol specific optimizations with the AcceptFilter directive.
+ * @return NULL on success, or an error message on failure
+ */
+
+AP_DECLARE(const char *) ap_alloc_listener(process_rec *process, char *addr,
+                                  apr_port_t port, int type, int protocol,
+                                  const char* proto);
+
+/**
  * Loop through the global ap_listen_rec list and create all of the required
  * sockets.  This executes the listen and bind on the sockets.
  * @param s The global server_rec
@@ -103,6 +123,9 @@
 AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd, void 
*dummy, const char *arg);
 AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy, 
                                                 int argc, char *const argv[]);
+AP_DECLARE_NONSTD(const char *) ap_set_udp_listener(cmd_parms *cmd,
+                                                    void *dummy, int argc,
+                                                    char *const argv[]);
 AP_DECLARE_NONSTD(const char *) ap_set_send_buffer_size(cmd_parms *cmd, void 
*dummy,
                                    const char *arg);
 AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
@@ -114,6 +137,10 @@
   "Maximum length of the queue of pending connections, as used by listen(2)"), 
\
 AP_INIT_TAKE_ARGV("Listen", ap_set_listener, NULL, RSRC_CONF, \
   "A port number or a numeric IP address and a port number, and an optional 
protocol"), \
+AP_INIT_TAKE_ARGV("ListenTCP", ap_set_listener, NULL, RSRC_CONF, \
+  "A port number or a numeric IP address and a port number, and an optional 
protocol"), \
+AP_INIT_TAKE_ARGV("ListenUDP", ap_set_udp_listener, NULL, RSRC_CONF, \
+  "A port number or a numeric IP address and a port number, and an optional 
protocol"), \
 AP_INIT_TAKE1("SendBufferSize", ap_set_send_buffer_size, NULL, RSRC_CONF, \
   "Send buffer size in bytes"), \
 AP_INIT_TAKE1("ReceiveBufferSize", ap_set_receive_buffer_size, NULL, \
Index: include/httpd.h
===================================================================
--- include/httpd.h     (revision 565699)
+++ include/httpd.h     (working copy)
@@ -1224,6 +1224,7 @@
     apr_bucket_brigade *buffered_bb;
     apr_size_t bytes_in;
     apr_size_t bytes_written;
+    int connected;
 } core_output_filter_ctx_t;
  
 typedef struct core_filter_ctx {
Index: server/core.c
===================================================================
--- server/core.c       (revision 565699)
+++ server/core.c       (working copy)
@@ -3778,6 +3778,7 @@
                                   apr_bucket_alloc_t *alloc)
 {
     apr_status_t rv;
+    int sd_type;
     conn_rec *c = (conn_rec *) apr_pcalloc(ptrans, sizeof(conn_rec));
 
     c->sbh = sbh;
@@ -3797,16 +3798,38 @@
         apr_socket_close(csd);
         return NULL;
     }
-
     apr_sockaddr_ip_get(&c->local_ip, c->local_addr);
-    if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
-        != APR_SUCCESS) {
+    
+    apr_socket_type_get(csd, &sd_type);
+    if (sd_type == SOCK_STREAM &&
+        (rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
+        != APR_SUCCESS)
+    {
         ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
                      "apr_socket_addr_get(APR_REMOTE)");
         apr_socket_close(csd);
         return NULL;
     }
-
+    if (sd_type != SOCK_STREAM) {
+        if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
+        != APR_SUCCESS)
+        {
+            char tmpbuf = 0;
+            apr_size_t tmpbuflen = 1;
+            /** Allocate a apr_sockaddr_t, since we can't use the one in csd */
+            c->remote_addr = apr_pcalloc(c->pool, sizeof(apr_sockaddr_t));
+            c->remote_addr->pool = c->pool;
+            if ((rv = apr_socket_recvfrom(c->remote_addr, csd, MSG_PEEK, 
+                                          &tmpbuf, &tmpbuflen))
+                != APR_SUCCESS)
+            {
+                ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
+                             "Error reading UDP peer");
+                /* Don't close non-connected UDP sockets */
+                return NULL;
+            }
+        }
+    }
     apr_sockaddr_ip_get(&c->remote_ip, c->remote_addr);
     c->base_server = server;
 
@@ -3828,6 +3851,7 @@
 static int core_pre_connection(conn_rec *c, void *csd)
 {
     core_net_rec *net = apr_palloc(c->pool, sizeof(*net));
+    int sd_type;
     apr_status_t rv;
 
 #ifdef AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
@@ -3838,13 +3862,17 @@
      * performance penalties.  (Failing to disable Nagle is not much of a
      * problem with simple HTTP.)
      */
-    rv = apr_socket_opt_set(csd, APR_TCP_NODELAY, 1);
-    if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
-        /* expected cause is that the client disconnected already,
-         * hence the debug level
-         */
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c,
-                      "apr_socket_opt_set(APR_TCP_NODELAY)");
+
+    apr_socket_type_get(csd, &sd_type);
+    if (sd_type == SOCK_STREAM) {
+        rv = apr_socket_opt_set(csd, APR_TCP_NODELAY, 1);
+        if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
+            /* expected cause is that the client disconnected already,
+             * hence the debug level
+             */
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c,
+                          "apr_socket_opt_set(APR_TCP_NODELAY)");
+        }
     }
 #endif
 
Index: server/core_filters.c
===================================================================
--- server/core_filters.c       (revision 565699)
+++ server/core_filters.c       (working copy)
@@ -311,6 +311,7 @@
                                       int make_a_copy, conn_rec *c);
 
 static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
+                                             int connected,
                                              apr_bucket_brigade *bb,
                                              apr_size_t *bytes_written,
                                              conn_rec *c);
@@ -318,11 +319,13 @@
 static void remove_empty_buckets(apr_bucket_brigade *bb);
 
 static apr_status_t send_brigade_blocking(apr_socket_t *s,
+                                          int connected,
                                           apr_bucket_brigade *bb,
                                           apr_size_t *bytes_written,
                                           conn_rec *c);
 
 static apr_status_t writev_nonblocking(apr_socket_t *s,
+                                       int connected,
                                        struct iovec *vec, apr_size_t nvec,
                                        apr_bucket_brigade *bb,
                                        apr_size_t *cumulative_bytes_written,
@@ -360,12 +363,23 @@
 
     if (ctx == NULL) {
         apr_status_t rv;
+        int sd_type;
         ctx = apr_pcalloc(c->pool, sizeof(*ctx));
+        ctx->connected = 1;
         net->out_ctx = (core_output_filter_ctx_t *)ctx;
         rv = apr_socket_opt_set(net->client_socket, APR_SO_NONBLOCK, 1);
         if (rv != APR_SUCCESS) {
             return rv;
         }
+        /* Check for non-connected socket */
+        apr_socket_type_get(net->client_socket, &sd_type);
+        if (sd_type != SOCK_STREAM) {
+            apr_sockaddr_t *peer;
+            if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE,
+                                          net->client_socket)) != APR_SUCCESS)
+                /* Disconnected socket */
+                ctx->connected = 0;
+        }
     }
 
     if (new_bb != NULL) {
@@ -419,7 +433,8 @@
      */
 
     if (new_bb == NULL) {
-        apr_status_t rv = send_brigade_nonblocking(net->client_socket, bb,
+        apr_status_t rv = send_brigade_nonblocking(net->client_socket,
+                                                   ctx->connected, bb,
                                                    &(ctx->bytes_written), c);
         if (APR_STATUS_IS_EAGAIN(rv)) {
             rv = APR_SUCCESS;
@@ -439,7 +454,8 @@
         next = APR_BUCKET_NEXT(bucket);
         if (APR_BUCKET_IS_FLUSH(bucket)) {
             apr_bucket_brigade *remainder = apr_brigade_split(bb, next);
-            apr_status_t rv = send_brigade_blocking(net->client_socket, bb,
+            apr_status_t rv = send_brigade_blocking(net->client_socket,
+                                                    ctx->connected, bb,
                                                     &(ctx->bytes_written), c);
             if (rv != APR_SUCCESS) {
                 /* The client has aborted the connection */
@@ -475,7 +491,8 @@
         /* ### Writing the entire brigade may be excessive; we really just
          * ### need to send enough data to be under THRESHOLD_MAX_BUFFER.
          */
-        apr_status_t rv = send_brigade_blocking(net->client_socket, bb,
+        apr_status_t rv = send_brigade_blocking(net->client_socket,
+                                                ctx->connected, bb,
                                                 &(ctx->bytes_written), c);
         if (rv != APR_SUCCESS) {
             /* The client has aborted the connection */
@@ -484,7 +501,8 @@
         }
     }
     else if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
-        apr_status_t rv = send_brigade_nonblocking(net->client_socket, bb,
+        apr_status_t rv = send_brigade_nonblocking(net->client_socket,
+                                                   ctx->connected, bb,
                                                    &(ctx->bytes_written), c);
         if ((rv != APR_SUCCESS) && (!APR_STATUS_IS_EAGAIN(rv))) {
             /* The client has aborted the connection */
@@ -535,12 +553,14 @@
 #endif
 
 static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
+                                             int connected,
                                              apr_bucket_brigade *bb,
                                              apr_size_t *bytes_written,
                                              conn_rec *c)
 {
     apr_bucket *bucket, *next;
     apr_status_t rv;
+    int type;
     struct iovec vec[MAX_IOVEC_TO_WRITE];
     apr_size_t nvec = 0;
 
@@ -552,7 +572,12 @@
         int did_sendfile = 0;
         next = APR_BUCKET_NEXT(bucket);
 #if APR_HAS_SENDFILE
-        if (APR_BUCKET_IS_FILE(bucket)) {
+        /** Don't even bother with sendfile if we're not dealing with a
+         *  SOCK_STREAM socket
+         */
+        if (APR_BUCKET_IS_FILE(bucket) &&
+            ((apr_socket_type_get(s, &type) == APR_SUCCESS) && 
+             (type == SOCK_STREAM))) {
             apr_bucket_file *file_bucket = (apr_bucket_file *)(bucket->data);
             apr_file_t *fd = file_bucket->fd;
             /* Use sendfile to send this file unless:
@@ -566,7 +591,8 @@
                 did_sendfile = 1;
                 if (nvec > 0) {
                     (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 1);
-                    rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, 
c);
+                    rv = writev_nonblocking(s, connected, vec, nvec, bb,
+                                            bytes_written, c);
                     nvec = 0;
                     if (rv != APR_SUCCESS) {
                         (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
@@ -597,7 +623,9 @@
             vec[nvec].iov_len = length;
             nvec++;
             if (nvec == MAX_IOVEC_TO_WRITE) {
-                rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
+                
+                rv = writev_nonblocking(s, connected, vec, nvec, bb,
+                                        bytes_written, c);
                 nvec = 0;
                 if (rv != APR_SUCCESS) {
                     return rv;
@@ -608,7 +636,7 @@
     }
 
     if (nvec > 0) {
-        rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
+        rv = writev_nonblocking(s, connected, vec, nvec, bb, bytes_written, c);
         if (rv != APR_SUCCESS) {
             return rv;
         }
@@ -630,6 +658,7 @@
 }
 
 static apr_status_t send_brigade_blocking(apr_socket_t *s,
+                                          int connected,
                                           apr_bucket_brigade *bb,
                                           apr_size_t *bytes_written,
                                           conn_rec *c)
@@ -638,7 +667,7 @@
 
     rv = APR_SUCCESS;
     while (!APR_BRIGADE_EMPTY(bb)) {
-        rv = send_brigade_nonblocking(s, bb, bytes_written, c);
+        rv = send_brigade_nonblocking(s, connected, bb, bytes_written, c);
         if (rv != APR_SUCCESS) {
             if (APR_STATUS_IS_EAGAIN(rv)) {
                 /* Wait until we can send more data */
@@ -665,6 +694,7 @@
 }
 
 static apr_status_t writev_nonblocking(apr_socket_t *s,
+                                       int connected,
                                        struct iovec *vec, apr_size_t nvec,
                                        apr_bucket_brigade *bb,
                                        apr_size_t *cumulative_bytes_written,
@@ -690,7 +720,12 @@
     offset = 0;
     while (bytes_written < bytes_to_write) {
         apr_size_t n = 0;
-        rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
+        if (connected) {
+            rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
+        } else {
+            rv = apr_socket_sendtov(s, c->remote_addr, 0, vec + offset,
+                                    nvec - offset, &n);
+        }
         if (n > 0) {
             bytes_written += n;
             for (i = offset; i < nvec; ) {
Index: server/listen.c
===================================================================
--- server/listen.c     (revision 565699)
+++ server/listen.c     (working copy)
@@ -49,6 +49,7 @@
     int v6only_setting = 1;
 #endif
 #endif
+    int type;
     apr_status_t stat;
 
 #ifndef WIN32
@@ -136,8 +137,17 @@
         apr_socket_close(s);
         return stat;
     }
-
-    if ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
+    
+    if ((stat = apr_socket_type_get(s, &type)) != APR_SUCCESS) {
+        ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p,
+                      "make_sock: could not determine socket type for "
+                      "address %pI", server->bind_addr);
+        apr_socket_close(s);
+        return stat;
+    }
+    
+    if (type == SOCK_STREAM &&
+        ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS)) {
         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p,
                       "make_sock: unable to listen for connections "
                       "on address %pI",
@@ -204,7 +214,8 @@
     const char *accf;
     apr_status_t rv;
     const char *proto;
-
+    int protocol;
+    
     proto = lis->protocol;
 
     if (!proto) {
@@ -225,10 +236,13 @@
         }
 #else
 #ifdef APR_TCP_DEFER_ACCEPT
-        rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 30);
-        if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
-            ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
-                              "Failed to enable APR_TCP_DEFER_ACCEPT");
+        rv = apr_socket_protocol_get(s, &protocol);
+        if ((rv == APR_SUCCESS) && (protocol == APR_PROTO_TCP)) {
+            rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 30);
+            if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
+                ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
+                                  "Failed to enable APR_TCP_DEFER_ACCEPT");
+            }
         }
 #endif
 #endif
@@ -241,12 +255,14 @@
     return APR_SUCCESS;
 }
 
-static const char *alloc_listener(process_rec *process, char *addr,
-                                  apr_port_t port, const char* proto)
+AP_DECLARE(const char *) ap_alloc_listener(process_rec *process, char *addr,
+                                  apr_port_t port, int type, int protocol,
+                                  const char* proto)
 {
     ap_listen_rec **walk, *last;
     apr_status_t status;
     apr_sockaddr_t *sa;
+    apr_socket_t *sd;
     int found_listener = 0;
 
     /* see if we've got an old listener for this address:port */
@@ -255,13 +271,20 @@
         /* Some listeners are not real so they will not have a bind_addr. */
         if (sa) {
             ap_listen_rec *new;
+            sd = (*walk)->sd;
             apr_port_t oldport;
+            int oldprotocol = -1;
+            int oldtype = -1;
 
             oldport = sa->port;
-            /* If both ports are equivalent, then if their names are 
equivalent,
+            apr_socket_type_get(sd, &oldtype);
+            apr_socket_protocol_get(sd, &oldprotocol);
+            /* If both ports are equivalent and both socket type/protocols are
+             * equivalent, then if their names are equivalent,
              * then we will re-use the existing record.
              */
-            if (port == oldport &&
+            if ((port == oldport &&
+                 (type == oldtype && protocol == oldprotocol)) &&
                 ((!addr && !sa->hostname) ||
                  ((addr && sa->hostname) && !strcmp(sa->hostname, addr)))) {
                 new = *walk;
@@ -284,7 +307,7 @@
                                         process->pool))
         != APR_SUCCESS) {
         ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
-                      "alloc_listener: failed to set up sockaddr for %s",
+                      "ap_alloc_listener: failed to set up sockaddr for %s",
                       addr);
         return "Listen setup failed";
     }
@@ -309,7 +332,7 @@
         sa = sa->next;
 
         status = apr_socket_create(&new->sd, new->bind_addr->family,
-                                    SOCK_STREAM, 0, process->pool);
+                                    type, protocol, process->pool);
 
 #if APR_HAVE_IPV6
         /* What could happen is that we got an IPv6 address, but this system
@@ -322,7 +345,7 @@
 #endif
         if (status != APR_SUCCESS) {
             ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
-                          "alloc_listener: failed to get a socket for %s",
+                          "ap_alloc_listener: failed to get a socket for %s",
                           addr);
             return "Listen setup failed";
         }
@@ -583,7 +606,6 @@
     ap_listenbacklog = DEFAULT_LISTENBACKLOG;
 }
 
-
 AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
                                                 int argc, char *const argv[])
 {
@@ -626,9 +648,57 @@
         ap_str_tolower(proto);
     }
 
-    return alloc_listener(cmd->server->process, host, port, proto);
+    return ap_alloc_listener(cmd->server->process, host, port,
+                             SOCK_STREAM, 0, proto);
 }
 
+AP_DECLARE_NONSTD(const char *) ap_set_udp_listener(cmd_parms *cmd,
+                                                    void *dummy, int argc,
+                                                    char *const argv[])
+{
+    char *host, *scope_id, *proto;
+    apr_port_t port;
+    apr_status_t rv;
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+    if (err != NULL) {
+        return err;
+    }
+
+    if (argc < 1 || argc > 2) {
+        return "Listen requires 1 or 2 arguments.";
+    }
+
+    rv = apr_parse_addr_port(&host, &scope_id, &port, argv[0], cmd->pool);
+    if (rv != APR_SUCCESS) {
+        return "Invalid address or port";
+    }
+
+    if (host && !strcmp(host, "*")) {
+        host = NULL;
+    }
+
+    if (scope_id) {
+        /* XXX scope id support is useful with link-local IPv6 addresses */
+        return "Scope id is not supported";
+    }
+
+    if (!port) {
+        return "Port must be specified";
+    }
+
+    if (argc != 2) {
+        proto = "http";
+    }
+    else {
+        proto = apr_pstrdup(cmd->pool, argv[1]);
+        ap_str_tolower(proto);
+    }
+
+    return ap_alloc_listener(cmd->server->process, host, port,
+                             SOCK_DGRAM, APR_PROTO_UDP, proto);
+}
+
 AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,
                                                      void *dummy,
                                                      const char *arg)
Index: server/mpm/experimental/event/event.c
===================================================================
--- server/mpm/experimental/event/event.c       (revision 565699)
+++ server/mpm/experimental/event/event.c       (working copy)
@@ -1019,7 +1019,9 @@
 
                 apr_pool_tag(ptrans, "transaction");
 
-                rc = lr->accept_func(&csd, lr, ptrans);
+                rc = APR_SUCCESS;
+                if (lr->accept_func)
+                    rc = lr->accept_func(&csd, lr, ptrans);
 
                 /* later we trash rv and rely on csd to indicate
                  * success/failure
Index: server/mpm/experimental/leader/leader.c
===================================================================
--- server/mpm/experimental/leader/leader.c     (revision 565699)
+++ server/mpm/experimental/leader/leader.c     (working copy)
@@ -786,7 +786,9 @@
         }
     got_fd:
         if (!workers_may_exit) {
-            rv = lr->accept_func(&csd, lr, ptrans);
+            rv = APR_SUCCESS;
+            if (lr->accept_func)
+                rv = lr->accept_func(&csd, lr, ptrans);
             /* later we trash rv and rely on csd to indicate success/failure */
             AP_DEBUG_ASSERT(rv == APR_SUCCESS || !csd);
 
@@ -1996,3 +1998,4 @@
     leader_hooks                /* register_hooks */
 };
 
+
Index: server/mpm/experimental/perchild/perchild.c
===================================================================
--- server/mpm/experimental/perchild/perchild.c (revision 565699)
+++ server/mpm/experimental/perchild/perchild.c (working copy)
@@ -736,7 +736,9 @@
         }
     got_fd:
         if (!workers_may_exit) {
-            rv = lr->accept_func(&csd, lr, ptrans);
+            rv = APR_SUCCESS;
+            if (lr->accept_func)
+                rv = lr->accept_func(&csd, lr, ptrans);
             if (rv == APR_EGENERAL) {
                 /* E[NM]FILE, ENOMEM, etc */
                 workers_may_exit = 1;
@@ -2049,3 +2051,4 @@
     perchild_hooks              /* register_hooks */
 };
 
+
Index: server/mpm/experimental/threadpool/threadpool.c
===================================================================
--- server/mpm/experimental/threadpool/threadpool.c     (revision 565699)
+++ server/mpm/experimental/threadpool/threadpool.c     (working copy)
@@ -855,7 +855,9 @@
             }
         }
         if (!listener_may_exit) {
-            rv = lr->accept_func(&csd, lr, ptrans);
+            rv = APR_SUCCESS;
+            if (lr->accept_func)
+                rv = lr->accept_func(&csd, lr, ptrans);
             /* later we trash rv and rely on csd to indicate success/failure */
             AP_DEBUG_ASSERT(rv == APR_SUCCESS || !csd);
 
@@ -2250,3 +2252,4 @@
     threadpool_hooks            /* register_hooks */
 };
 
+
Index: server/mpm/prefork/prefork.c
===================================================================
--- server/mpm/prefork/prefork.c        (revision 565699)
+++ server/mpm/prefork/prefork.c        (working copy)
@@ -450,6 +450,7 @@
     ap_sb_handle_t *sbh;
     apr_bucket_alloc_t *bucket_alloc;
     int last_poll_idx = 0;
+    int sd_type;
 
     mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
                                    * child initializes
@@ -491,24 +492,6 @@
 
     ap_create_sb_handle(&sbh, pchild, my_child_num, 0);
 
-    (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL);
-
-    /* Set up the pollfd array */
-    /* ### check the status */
-    (void) apr_pollset_create(&pollset, num_listensocks, pchild, 0);
-
-    for (lr = ap_listeners, i = num_listensocks; i--; lr = lr->next) {
-        apr_pollfd_t pfd = { 0 };
-
-        pfd.desc_type = APR_POLL_SOCKET;
-        pfd.desc.s = lr->sd;
-        pfd.reqevents = APR_POLLIN;
-        pfd.client_data = lr;
-
-        /* ### check the status */
-        (void) apr_pollset_add(pollset, &pfd);
-    }
-
     mpm_state = AP_MPMQ_RUNNING;
 
     bucket_alloc = apr_bucket_alloc_create(pchild);
@@ -540,6 +523,34 @@
         /* Lock around "accept", if necessary */
         SAFE_ACCEPT(accept_mutex_on());
 
+        /* Set up the pollfd array - we waste cycles on doing it inside the 
+         * loop, but we've got to do it this way to lock the non-accept() 
+         * ports */
+        /* ### check the status */
+        (void) apr_pollset_create(&pollset, num_listensocks, pchild, 0);
+
+        for (lr = ap_listeners, i = num_listensocks; i--; lr = lr->next) {
+            apr_pollfd_t pfd = { 0 };
+            /** FIXME: We currently use this as a hack to see if we're 
actively 
+             *  operating on a UDP socket, since there's no accept()
+             *  
+             *  There's a really nasty gotcha here: since we only unlock the 
UDP
+             *  port at the end of the operation, but by that point another 
child
+             *  will already be polling the sockets, we won't start polling the
+             *  UDP port until after the next connection is received (and the 
next
+             *  child starts polling) */
+            if (lr->active != 1)
+                continue;
+
+            pfd.desc_type = APR_POLL_SOCKET;
+            pfd.desc.s = lr->sd;
+            pfd.reqevents = APR_POLLIN;
+            pfd.client_data = lr;
+
+            /* ### check the status */
+            (void) apr_pollset_add(pollset, &pfd);
+        }
+
         if (num_listensocks == 1) {
             /* There is only one listener record, so refer to that one. */
             lr = ap_listeners;
@@ -598,8 +609,24 @@
         /* if we accept() something we don't want to die, so we have to
          * defer the exit
          */
-        status = lr->accept_func(&csd, lr, ptrans);
 
+        status = APR_SUCCESS;
+        
+        /** Check for non-stream socket + lock */
+        apr_socket_type_get(lr->sd, &sd_type);
+
+        if (sd_type == SOCK_STREAM) {
+            status = lr->accept_func(&csd, lr, ptrans);
+        } else {
+            /** TODO: Tie this worker to this socket permanently - or come up
+             *  with a better way to ensure that a subsequent request can be
+             *  polled */
+            csd = lr->sd;
+            lr->active = 2; 
+        }
+
+        /** Release the pollset */
+        apr_pollset_destroy(pollset);        
         SAFE_ACCEPT(accept_mutex_off());      /* unlock after "accept" */
 
         if (status == APR_EGENERAL) {
@@ -618,7 +645,9 @@
         current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd, 
my_child_num, sbh, bucket_alloc);
         if (current_conn) {
             ap_process_connection(current_conn, csd);
-            ap_lingering_close(current_conn);
+            if (sd_type == SOCK_STREAM) {
+                ap_lingering_close(current_conn);
+            } 
         }
 
         /* Check the pod and the generation number after processing a
@@ -636,6 +665,9 @@
              */
             die_now = 1;
         }
+
+        /** Unlock socket, if needed.  No need to lock this */
+        lr->active = 1;
     }
     clean_child_exit(0);
 }
Index: server/mpm/worker/worker.c
===================================================================
--- server/mpm/worker/worker.c  (revision 565699)
+++ server/mpm/worker/worker.c  (working copy)
@@ -737,7 +737,9 @@
                 apr_allocator_owner_set(allocator, ptrans);
             }
             apr_pool_tag(ptrans, "transaction");
-            rv = lr->accept_func(&csd, lr, ptrans);
+            rv = APR_SUCCESS;
+            if (lr->accept_func)
+                rv = lr->accept_func(&csd, lr, ptrans);
             /* later we trash rv and rely on csd to indicate success/failure */
             AP_DEBUG_ASSERT(rv == APR_SUCCESS || !csd);
 
@@ -2340,3 +2342,4 @@
     worker_hooks                /* register_hooks */
 };
 
+
Index: server/mpm_common.c
===================================================================
--- server/mpm_common.c (revision 565699)
+++ server/mpm_common.c (working copy)
@@ -480,6 +480,13 @@
      *
      * In spite of these problems, failure here is not a shooting offense.
      */
+
+    int protocol;
+    if (!((apr_socket_protocol_get(s, &protocol) == APR_SUCCESS) &&
+          protocol == APR_PROTO_TCP))
+        /** Don't do anything unless we're a TCP socket */
+        return;
+    
     apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
 
     if (status != APR_SUCCESS) {
@@ -643,6 +650,13 @@
     apr_socket_t *sock;
     apr_pool_t *p;
     apr_size_t len;
+    int protocol;
+    int type;
+    
+    if (apr_socket_type_get(ap_listeners->sd, &type) != APR_SUCCESS)
+        type = SOCK_STREAM;
+    if (apr_socket_protocol_get(ap_listeners->sd, &protocol) != APR_SUCCESS)
+        type = 0;
 
     /* create a temporary pool for the socket.  pconf stays around too long */
     rv = apr_pool_create(&p, pod->p);
@@ -651,7 +665,7 @@
     }
 
     rv = apr_socket_create(&sock, ap_listeners->bind_addr->family,
-                           SOCK_STREAM, 0, p);
+                           type, protocol, p);
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
                      "get socket to connect to listener");
Index: include/apr_network_io.h
===================================================================
--- include/apr_network_io.h    (revision 572605)
+++ include/apr_network_io.h    (working copy)
@@ -508,6 +508,33 @@
                                             apr_size_t *len);
 
 /**
+ * Send multiple packets of data over a network.
+ * @param sock The socket to send the data over.
+ * @param where The apr_sockaddr_t describing where to send the data
+ * @param flags The flags to use
+ * @param vec The array of iovec structs containing the data to send 
+ * @param nvec The number of iovec structs in the array
+ * @param len Receives the number of bytes actually written
+ * @remark
+ * <PRE>
+ * This functions acts like a blocking write by default.  To change 
+ * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+ * socket option.
+ * The number of bytes actually sent is stored in argument 3.
+ *
+ * It is possible for both bytes to be sent and an error to be returned.
+ *
+ * APR_EINTR is never returned.
+ * </PRE>
+ */
+APR_DECLARE(apr_status_t) apr_socket_sendtov(apr_socket_t *sock,
+                                             apr_sockaddr_t *where,
+                                             apr_int32_t flags,
+                                             const struct iovec *vec,
+                                             apr_int32_t nvec,
+                                             apr_size_t *len);
+
+/**
  * Read data from a socket.  On success, the address of the peer from
  * which the data was sent is copied into the @param from parameter,
  * and the @param len parameter is updated to give the number of bytes
 
Index: network_io/unix/sendrecv.c
===================================================================
--- network_io/unix/sendrecv.c  (revision 572605)
+++ network_io/unix/sendrecv.c  (working copy)
@@ -184,7 +184,15 @@
     return APR_SUCCESS;
 }
 
-apr_status_t apr_socket_sendv(apr_socket_t * sock, const struct iovec *vec,
+apr_status_t apr_socket_sendtov(apr_socket_t *sock, apr_sockaddr_t *where,
+                                apr_int32_t flags, const struct iovec *vec,
+                                apr_int32_t nvec, apr_size_t *len)
+{
+    *len = vec[0].iov_len;
+    return apr_socket_sendto(sock, where, flags, vec[0].iov_base, len);
+}
+
+apr_status_t apr_socket_sendv(apr_socket_t *sock, const struct iovec *vec,
                               apr_int32_t nvec, apr_size_t *len)
 {
 #ifdef HAVE_WRITEV

Reply via email to