]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
first try at fr_bio_fd_accept()
authorAlan T. DeKok <aland@freeradius.org>
Tue, 19 Nov 2024 20:16:47 +0000 (15:16 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 19 Nov 2024 20:20:30 +0000 (15:20 -0500)
which creates a new BIO from a parent of type LISTEN

src/lib/bio/fd.c
src/lib/bio/fd.h
src/lib/bio/fd_open.c
src/lib/bio/fd_priv.h
src/lib/bio/network.c
src/listen/control/proto_control_unix.c

index dadf3040993c09ff04d1e50e6894a3a239af41f5..fe298d4430a53eb33a97ae310ff7bfb5b9b5891c 100644 (file)
@@ -961,7 +961,7 @@ retry:
 }
 
 
-int fr_bio_fd_init_accept(fr_bio_fd_t *my)
+int fr_bio_fd_init_listen(fr_bio_fd_t *my)
 {
        my->bio.read = fr_bio_fd_read_accept;
        my->bio.write = fr_bio_null_write;
@@ -1262,7 +1262,7 @@ int fr_bio_fd_connect_full(fr_bio_t *bio, fr_event_list_t *el, fr_bio_callback_t
         *      The caller may just call us without caring about what the underlying BIO is.  In which case we
         *      need to be safe.
         */
-       if ((my->info.socket.af == AF_FILE_BIO) || (my->info.type == FR_BIO_FD_ACCEPT)) {
+       if ((my->info.socket.af == AF_FILE_BIO) || (my->info.type == FR_BIO_FD_LISTEN)) {
                fr_bio_fd_set_open(my);
                goto connected;
        }
@@ -1363,6 +1363,7 @@ int fr_bio_fd_write_only(fr_bio_t *bio)
                break;
 
        case FR_BIO_FD_CONNECTED:
+       case FR_BIO_FD_ACCEPTED:
                /*
                 *      Further reads are disallowed.
                 */
@@ -1372,7 +1373,7 @@ int fr_bio_fd_write_only(fr_bio_t *bio)
                }
                break;
 
-       case FR_BIO_FD_ACCEPT:
+       case FR_BIO_FD_LISTEN:
                fr_strerror_const("Only unconnected sockets can be marked 'write-only'");
                return -1;
        }
@@ -1380,3 +1381,106 @@ int fr_bio_fd_write_only(fr_bio_t *bio)
        my->bio.read = fr_bio_fd_read_discard;
        return 0;
 }
+
+/** Alternative to calling fr_bio_read() on new socket.
+ *
+ */
+int fr_bio_fd_accept(TALLOC_CTX *ctx, fr_bio_t **out_p, fr_bio_t *bio)
+{
+       int fd, tries = 0;
+       int rcode;
+       fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
+       socklen_t salen;
+       struct sockaddr_storage sockaddr;
+       fr_bio_fd_t *out;
+       fr_bio_fd_config_t *cfg;
+
+       salen = sizeof(sockaddr);
+       *out_p = NULL;
+
+       fr_assert(my->info.type == FR_BIO_FD_LISTEN);
+       fr_assert(my->info.socket.type == SOCK_STREAM);
+
+retry:
+#ifdef __linux__
+       /*
+        *      Set these flags immediately on the new socket.
+        */
+       fd = accept4(my->info.socket.fd, (struct sockaddr *) &sockaddr, &salen, SOCK_NONBLOCK | SOCK_CLOEXEC);
+#else
+       fd = accept(my->info.socket.fd, (struct sockaddr *) &sockaddr, &salen);
+#endif
+       if (fd < 0) {
+               switch (errno) {
+               case EINTR:
+                       /*
+                        *      Try a few times before giving up.
+                        */
+                       tries++;
+                       if (tries <= my->max_tries) goto retry;
+                       return 0;
+
+                       /*
+                        *      We can ignore these errors.
+                        */
+               case ECONNABORTED:
+#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
+               case EWOULDBLOCK:
+#endif
+               case EAGAIN:
+#ifdef EPERM
+               case EPERM:
+#endif
+#ifdef ETIMEDOUT
+               case ETIMEDOUT:
+#endif
+                       return 0;
+
+               default:
+                       /*
+                        *      Some other error, it's fatal.
+                        */
+                       fr_bio_shutdown(&my->bio);
+                       break;
+               }
+
+               return fr_bio_error(IO);
+       }
+
+       /*
+        *      Allocate the base BIO and set it up.
+        */
+       out = (fr_bio_fd_t *) fr_bio_fd_alloc(ctx, NULL, my->offset);
+       if (!out) {
+               close(fd);
+               return fr_bio_error(GENERIC);
+       }
+
+       /*
+        *      We have a file descriptor.  Initialize the configuration with the new information.
+        */
+       cfg = talloc_memdup(out, my->info.cfg, sizeof(*my->info.cfg));
+       if (!cfg) {
+               fr_strerror_const("Out of memory");
+               close(fd);
+               talloc_free(out);
+               return fr_bio_error(GENERIC);
+       }
+
+       /*
+        *      Set the type to ACCEPTED, and set up the rest of the callbacks to match.
+        */
+       cfg->type = FR_BIO_FD_ACCEPTED;
+       out->info.socket.fd = fd;
+
+       rcode = fr_bio_fd_open(bio, cfg);
+       if (rcode < 0) {
+               talloc_free(out);
+               return rcode;
+       }
+
+       fr_assert(out->info.type == FR_BIO_FD_CONNECTED);
+
+       *out_p = (fr_bio_t *) out;
+       return 1;
+}
index 6bbdf83a60e69afb7d763968782d0266f7eacb92..de40208e60fb7631c9c520dca58c311ecbeb4dd9 100644 (file)
@@ -63,8 +63,9 @@ typedef enum {
                                        // updates #fr_bio_fd_packet_ctx_t for reads,
                                        // uses #fr_bio_fd_packet_ctx_t for writes
        FR_BIO_FD_CONNECTED,            //!< connected client sockets (UDP or TCP)
-       FR_BIO_FD_ACCEPT,               //!< returns new fd in buffer on fr_bio_read()
+       FR_BIO_FD_LISTEN,               //!< returns new fd in buffer on fr_bio_read() or fr_bio_fd_accept()
                                        // updates #fr_bio_fd_packet_ctx_t on successful FD read.
+       FR_BIO_FD_ACCEPTED,             //!< temporarily until it's connected.
 } fr_bio_fd_type_t;
 
 /** Configuration for sockets
@@ -139,3 +140,5 @@ int         fr_bio_fd_open(fr_bio_t *bio, fr_bio_fd_config_t const *cfg) CC_HINT(nonnul
 int            fr_bio_fd_write_only(fr_bio_t *bio) CC_HINT(nonnull);
 
 int            fr_bio_fd_reopen(fr_bio_t *bio) CC_HINT(nonnull);
+
+int            fr_bio_fd_accept(TALLOC_CTX *ctx, fr_bio_t **out, fr_bio_t *bio)  CC_HINT(nonnull);
index 899e313a7eb84a64eec4303e3fffa490228d6476..c9ec70f70cf00f6aa184b9a0a93220351986d7db 100644 (file)
@@ -751,10 +751,19 @@ int fr_bio_fd_open(fr_bio_t *bio, fr_bio_fd_config_t const *cfg)
                        }
                }
 
-               fd = socket(my->info.socket.af, my->info.socket.type, protocol);
-               if (fd < 0) {
-                       fr_strerror_printf("Failed opening socket: %s", fr_syserror(errno));
-                       return -1;
+               /*
+                *      It's already opened, so we don't need to do that.
+                */
+               if (cfg->type == FR_BIO_FD_ACCEPTED) {
+                       fd = my->info.socket.fd;
+                       fr_assert(fd >= 0);
+
+               } else {
+                       fd = socket(my->info.socket.af, my->info.socket.type, protocol);
+                       if (fd < 0) {
+                               fr_strerror_printf("Failed opening socket: %s", fr_syserror(errno));
+                               return -1;
+                       }
                }
 
        } else if (cfg->path) {
@@ -928,10 +937,31 @@ int fr_bio_fd_open(fr_bio_t *bio, fr_bio_fd_config_t const *cfg)
                if (fr_bio_fd_init_connected(my) < 0) goto fail;
                break;
 
+       case FR_BIO_FD_ACCEPTED:
+#ifdef SO_NOSIGPIPE
+               /*
+                *      Although the server ignore SIGPIPE, some operating systems like BSD and OSX ignore the
+                *      ignoring.
+                *
+                *      Fortunately, those operating systems usually support SO_NOSIGPIPE.  We set that to prevent
+                *      them raising the signal in the first place.
+                */
+               {
+                       int on = 1;
+
+                       setsockopt(my->info.socket.fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
+               }
+#endif
+
+               my->info.type = FR_BIO_FD_CONNECTED;
+
+                if (fr_bio_fd_init_common(my) < 0) goto fail;
+               break;
+
                /*
                 *      Server socket which listens for new stream connections
                 */
-       case FR_BIO_FD_ACCEPT:
+       case FR_BIO_FD_LISTEN:
                fr_assert(my->info.socket.type == SOCK_STREAM);
 
                switch (my->info.socket.af) {
@@ -956,7 +986,7 @@ int fr_bio_fd_open(fr_bio_t *bio, fr_bio_fd_config_t const *cfg)
                        goto fail;
                }
 
-               if (fr_bio_fd_init_accept(my) < 0) goto fail;
+               if (fr_bio_fd_init_listen(my) < 0) goto fail;
                break;
        }
 
index 00c981f26f5d220cc1dc39850df4289fb2af34a0..def9c60b8288f670135185fce3b390545151b60b 100644 (file)
@@ -64,6 +64,6 @@ int   fr_bio_fd_init_common(fr_bio_fd_t *my);
 
 int    fr_bio_fd_init_connected(fr_bio_fd_t *my);
 
-int    fr_bio_fd_init_accept(fr_bio_fd_t *my);
+int    fr_bio_fd_init_listen(fr_bio_fd_t *my);
 
 int    fr_bio_fd_socket_name(fr_bio_fd_t *my);
index 48fda491369cb10dc471045f89e858d6976eed62..706c10c109be7e1a9b9568c193153b72559c6278 100644 (file)
@@ -133,9 +133,10 @@ fr_bio_t *fr_bio_network_alloc(TALLOC_CTX *ctx, fr_ipaddr_t const *allow, fr_ipa
                break;
 
        case FR_BIO_FD_CONNECTED:
+       case FR_BIO_FD_ACCEPTED:
                return NULL;
 
-       case FR_BIO_FD_ACCEPT:
+       case FR_BIO_FD_LISTEN:
                break;
        }
 
index 0c3acde40a47b3a82baa1f936cd457f9eecb4356..f76145658fe4765af75814a5420b7505f7cb7f8b 100644 (file)
@@ -412,7 +412,7 @@ static int mod_open(fr_listen_t *li)
        fr_assert(!thread->connection);
 
        cfg = (fr_bio_fd_config_t) {
-               .type = FR_BIO_FD_ACCEPT,
+               .type = FR_BIO_FD_LISTEN,
                .socket_type = SOCK_STREAM,
                .path = inst->filename,
                .uid = inst->uid,