The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
fd_errno.h
Go to the documentation of this file.
1#pragma once
2/*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16 */
17
18/**
19 * $Id: 5976737ab264d0c0328b9513ddc53af68cda5b50 $
20 * @file lib/bio/fd_errno.h
21 * @brief Common errno handling for FD bio reads and writes.
22 *
23 * @copyright 2024 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSIDH(lib_bio_fd_errno_h, "$Id: 5976737ab264d0c0328b9513ddc53af68cda5b50 $")
26
27#include <freeradius-devel/bio/fd_priv.h>
28
29/** Turn a failed read / write into a bio return code.
30 *
31 * Only called when the syscall failed, i.e. when rcode is negative.
32 *
33 * @param my the FD bio.
34 * @param[in,out] rcode_p in: the failed syscall return code. out: what to return to the application.
35 * @param[in,out] tries_p how many times we have retried on EINTR.
36 * @param is_write true for the write path, false for the read path.
37 * @return
38 * - false return *rcode_p to the application.
39 * - true the syscall was interrupted, redo it.
40 */
41static inline CC_HINT(always_inline) bool fr_bio_fd_errno_retry(fr_bio_fd_t *my, ssize_t *rcode_p, int *tries_p, bool is_write)
42{
43 bool *blocked = is_write ? &my->info.write_blocked : &my->info.read_blocked;
44 fr_bio_io_t blocked_cb = is_write ? my->cb.write_blocked : my->cb.read_blocked;
45
46 switch (errno) {
47 case EINTR:
48 /*
49 * Try a few times before giving up.
50 */
51 (*tries_p)++;
52 if (*tries_p <= my->max_tries) return true;
53
54 *rcode_p = fr_bio_error(IO);
55 return false;
56
57#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
58 case EWOULDBLOCK:
59#endif
60 case EAGAIN:
61 /*
62 * The operation would block, return that.
63 */
64 if (!*blocked) {
65 int error = 0;
66
67 *blocked = true;
68
69 /*
70 * The application sets write blocked / resume callbacks on the head of the BIO
71 * chain, not on the FD BIO. So we have to start the "we're blocked"
72 * notifications at the head. fr_bio_write_blocked() walks up the chain to do
73 * that.
74 */
75 if (is_write) {
76 error = fr_bio_write_blocked((fr_bio_t *) my);
77
78 } else if (blocked_cb) {
79 error = blocked_cb((fr_bio_t *) my);
80 }
81
82 if (error < 0) {
83 *rcode_p = error;
84 return false;
85 }
86 }
87
88 *rcode_p = fr_bio_error(IO_WOULD_BLOCK);
89 return false;
90
91 /*
92 * We're reading / writing a connected UDP socket, and the other end has gone away.
93 */
94 case ENOTCONN:
95
96 /*
97 * The other end of a socket has closed the connection.
98 */
99 case ECONNRESET:
100
101 /*
102 * The other end of a pipe has closed the connection.
103 */
104 case EPIPE:
105 /*
106 * The connection is no longer usable, so we call shutdown, rather than just marking it
107 * as being at EOF.
108 */
109 (void) fr_bio_shutdown(&my->bio);
110 *rcode_p = 0;
111 return false;
112
113 /*
114 * PMTU has been exceeded. Return a generic IO error.
115 *
116 * Only a write can exceed the PMTU, so a read which somehow reports EMSGSIZE is fatal.
117 *
118 * @todo - do this only for connected UDP sockets.
119 *
120 * However, recvmsg() can also return EMSGSIZE, if the msg_iovlen field has a bad value.
121 */
122 case EMSGSIZE:
123 *rcode_p = fr_bio_error(IO);
124 return false;
125
126 /*
127 * The underlying network has gone away. This is a fatal error for connected sockets,
128 * but a recoverable one for unconnected sockets.
129 */
130 case ENETDOWN:
131 case ENETUNREACH:
132 *rcode_p = fr_bio_error(IO);
133 return false;
134
135 default:
136 /*
137 * Some other error, it's fatal.
138 */
139 break;
140 }
141
142 /*
143 * Shut down the BIO. It's no longer useable.
144 */
145 (void) fr_bio_shutdown(&my->bio);
146
147 *rcode_p = fr_bio_error(IO);
148 return false;
149}
150
151/*
152 * Common code to suppress network failures on sendto / sendfromto for unconnected UDP sockets.
153 *
154 * If the destination network is down or is unreachable, we want to simply discard the packet. The error
155 * isn't fatal, so we don't close the socket.
156 */
157#define FD_ENET_SUPPRESS \
158 do { \
159 if ((rcode == fr_bio_error(IO)) && ((errno == ENETDOWN) || (errno == ENETUNREACH))) return 0; \
160 } while (0)
int(* fr_bio_io_t)(fr_bio_t *bio)
Definition base.h:84
#define fr_bio_error(_x)
Definition base.h:200
#define RCSIDH(h, id)
Definition build.h:604
bool read_blocked
did we block on read?
Definition fd.h:152
bool write_blocked
did we block on write?
Definition fd.h:153
static bool fr_bio_fd_errno_retry(fr_bio_fd_t *my, ssize_t *rcode_p, int *tries_p, bool is_write)
Turn a failed read / write into a bio return code.
Definition fd_errno.h:41
fr_bio_fd_info_t info
Definition fd_priv.h:43
Our FD bio structure.
Definition fd_priv.h:40
int fr_bio_write_blocked(fr_bio_t *bio)
Internal BIO function to tell all BIOs that it's blocked.
Definition base.c:354
int fr_bio_shutdown(fr_bio_t *bio)
Shut down a set of BIOs.
Definition base.c:158
long int ssize_t