Coverage Report

Created: 2026-02-26 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/freeradius-devel/util/socket.h
Line
Count
Source
1
#pragma once
2
3
/*
4
 *   This program is free software; you can redistribute it and/or modify
5
 *   it under the terms of the GNU General Public License as published by
6
 *   the Free Software Foundation; either version 2 of the License, or (at
7
 *   your option) any later version.
8
 *
9
 *   This program is distributed in the hope that it will be useful,
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *   GNU General Public License for more details.
13
 *
14
 *   You should have received a copy of the GNU General Public License
15
 *   along with this program; if not, write to the Free Software
16
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17
 */
18
19
/** Functions for establishing and managing low level sockets
20
 *
21
 * @file src/lib/util/socket.c
22
 *
23
 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24
 * @author Alan DeKok (aland@freeradius.org)
25
 *
26
 * @copyright 2015 The FreeRADIUS project
27
 */
28
RCSIDH(socket_h, "$Id: 6a6460f79064afa6d29cc8c117a24a719ab3b6a7 $")
29
30
#ifdef __cplusplus
31
extern "C" {
32
#endif
33
34
#include <freeradius-devel/build.h>
35
#include <freeradius-devel/missing.h>
36
#include <freeradius-devel/util/inet.h>
37
#include <freeradius-devel/util/time.h>
38
39
#include <stdbool.h>
40
#include <stdint.h>
41
#include <string.h>
42
#include <sys/time.h>
43
44
#ifdef HAVE_SYS_UN_H
45
#  include <sys/un.h>
46
/*
47
 *  The linux headers define the macro as:
48
 *
49
 * # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)        \
50
 *                      + strlen ((ptr)->sun_path))
51
 *
52
 * Which trips UBSAN, because it sees an operation on a NULL pointer.
53
 */
54
#  undef SUN_LEN
55
0
#  define SUN_LEN(su)  (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
56
#endif
57
58
/** Holds information necessary for binding or connecting to a socket.
59
 *
60
 * May also be used in protocol contexts to store information necessary for
61
 * returning packets to their originators.
62
 */
63
typedef struct {
64
  union {
65
    struct {
66
      int   ifindex;  //!< Source interface to bind to or originate the packet from.
67
      uint16_t  src_port; //!< Port to bind to, or originate the packet from.
68
      uint16_t  dst_port; //!< Port to connect to or send the packet to.
69
70
      fr_ipaddr_t src_ipaddr; //!< IP address to bind to, or originate the packet from.
71
      fr_ipaddr_t dst_ipaddr; //!< IP address to connect to, or send the packet to.
72
    } inet;
73
74
    struct {
75
      char const  *path;    //!< Unix socket path.
76
    } unix;
77
  };
78
  int af;     //!< AF_INET, AF_INET6, or AF_UNIX
79
  int type;   //!< SOCK_STREAM, SOCK_DGRAM, etc.
80
81
  int fd;     //!< File descriptor if this is a live socket.
82
} fr_socket_t;
83
84
/** Check the proto value is sane/supported
85
 *
86
 * @param[in] proto to check
87
 * @return
88
 *  - true if it is.
89
 *  - false if it's not.
90
 */
91
static inline bool fr_socket_proto_is_known(int proto)
92
0
{
93
  /*
94
   *  Check the protocol is sane
95
   */
96
0
  switch (proto) {
97
0
  case IPPROTO_UDP:
98
0
  case IPPROTO_TCP:
99
0
#ifdef IPPROTO_SCTP
100
0
  case IPPROTO_SCTP:
101
0
#endif
102
0
    return true;
103
104
0
  default:
105
0
    fr_strerror_printf("Unknown IP protocol %d", proto);
106
0
    return false;
107
0
  }
108
0
}
Unexecuted instantiation: dl.c:fr_socket_proto_is_known
Unexecuted instantiation: packet.c:fr_socket_proto_is_known
Unexecuted instantiation: socket.c:fr_socket_proto_is_known
Unexecuted instantiation: udp.c:fr_socket_proto_is_known
Unexecuted instantiation: udp_queue.c:fr_socket_proto_is_known
Unexecuted instantiation: base.c:fr_socket_proto_is_known
Unexecuted instantiation: decode.c:fr_socket_proto_is_known
Unexecuted instantiation: encode.c:fr_socket_proto_is_known
Unexecuted instantiation: raw.c:fr_socket_proto_is_known
Unexecuted instantiation: udp.c:fr_socket_proto_is_known
Unexecuted instantiation: list.c:fr_socket_proto_is_known
Unexecuted instantiation: tcp.c:fr_socket_proto_is_known
Unexecuted instantiation: abinary.c:fr_socket_proto_is_known
Unexecuted instantiation: vmps.c:fr_socket_proto_is_known
109
110
#define FR_SOCKET_ADDR_ALLOC_DEF_FUNC(_func, ...) \
111
  fr_socket_t *addr; \
112
  addr = talloc(ctx, fr_socket_t); \
113
  if (unlikely(!addr)) return NULL; \
114
  return _func(addr, ##__VA_ARGS__);
115
116
/** Swap src/dst information of a fr_socket_t
117
 *
118
 * @param[out] dst  Where to write the swapped addresses. May be the same as src.
119
 * @param[in] src Socket address to swap.
120
 */
121
static inline void fr_socket_addr_swap(fr_socket_t *dst, fr_socket_t const *src)
122
0
{
123
0
  fr_socket_t tmp = *src;
124
125
0
  if (dst != src) *dst = tmp; /* copy non-address fields over */
126
127
0
  dst->inet.dst_ipaddr = tmp.inet.src_ipaddr;
128
0
  dst->inet.dst_port = tmp.inet.src_port;
129
0
  dst->inet.src_ipaddr = tmp.inet.dst_ipaddr;
130
0
  dst->inet.src_port = tmp.inet.dst_port;
131
0
}
Unexecuted instantiation: dl.c:fr_socket_addr_swap
Unexecuted instantiation: packet.c:fr_socket_addr_swap
Unexecuted instantiation: socket.c:fr_socket_addr_swap
Unexecuted instantiation: udp.c:fr_socket_addr_swap
Unexecuted instantiation: udp_queue.c:fr_socket_addr_swap
Unexecuted instantiation: base.c:fr_socket_addr_swap
Unexecuted instantiation: decode.c:fr_socket_addr_swap
Unexecuted instantiation: encode.c:fr_socket_addr_swap
Unexecuted instantiation: raw.c:fr_socket_addr_swap
Unexecuted instantiation: list.c:fr_socket_addr_swap
Unexecuted instantiation: tcp.c:fr_socket_addr_swap
Unexecuted instantiation: abinary.c:fr_socket_addr_swap
Unexecuted instantiation: vmps.c:fr_socket_addr_swap
132
133
/** Initialise a fr_socket_t for connecting to a remote host using a specific src interface, address and port
134
 *
135
 * Can also be used to record information from an incoming packet so that we can
136
 * identify the correct return path later.
137
 *
138
 * @param[out] addr   to initialise.
139
 * @param[in] proto   one of the IPPROTO_* macros, i.e. IPPROTO_TCP, IPPROTO_UDP
140
 * @param[in] ifindex   The interface to originate the packet from Pass <= 0 to
141
 *        indicate an unknown or unspecified interface.
142
 * @param[in] src_ipaddr  The source IP address of the packet, or source interface for
143
 *        packets to egress out of.
144
 * @param[in] src_port    The source port of the packet or the source
145
 * @param[in] dst_ipaddr  The destination IP address of the packet.
146
 * @param[in] dst_port    The destination port of the packet.
147
 * @return
148
 *  - NULL if invalid parameters are provided.
149
 *  - An initialised fr_socket_t struct.
150
 */
151
static inline fr_socket_t *fr_socket_addr_init_inet(fr_socket_t *addr,
152
                int proto,
153
                int ifindex, fr_ipaddr_t const *src_ipaddr, int src_port,
154
                fr_ipaddr_t const *dst_ipaddr, int dst_port)
155
0
{
156
0
  if (!fr_socket_proto_is_known(proto)) return NULL;
157
0
158
0
  *addr = (fr_socket_t){
159
0
    .af = src_ipaddr->af,
160
0
    .type = (proto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM,
161
0
    .inet = {
162
0
      .ifindex = ifindex,
163
0
      .src_ipaddr = *src_ipaddr,
164
0
      .src_port = src_port,
165
0
      .dst_ipaddr = *dst_ipaddr,
166
0
      .dst_port = dst_port
167
0
    }
168
0
  };
169
0
170
0
  return addr;
171
0
}
Unexecuted instantiation: dl.c:fr_socket_addr_init_inet
Unexecuted instantiation: packet.c:fr_socket_addr_init_inet
Unexecuted instantiation: socket.c:fr_socket_addr_init_inet
Unexecuted instantiation: udp.c:fr_socket_addr_init_inet
Unexecuted instantiation: udp_queue.c:fr_socket_addr_init_inet
Unexecuted instantiation: base.c:fr_socket_addr_init_inet
Unexecuted instantiation: decode.c:fr_socket_addr_init_inet
Unexecuted instantiation: encode.c:fr_socket_addr_init_inet
Unexecuted instantiation: raw.c:fr_socket_addr_init_inet
Unexecuted instantiation: list.c:fr_socket_addr_init_inet
Unexecuted instantiation: tcp.c:fr_socket_addr_init_inet
Unexecuted instantiation: abinary.c:fr_socket_addr_init_inet
Unexecuted instantiation: vmps.c:fr_socket_addr_init_inet
172
173
/** Initialise a fr_socket_t for connecting to a remote host using a specific src interface, address and port
174
 *
175
 * Can also be used to record information from an incoming packet so that we can
176
 * identify the correct return path later.
177
 *
178
 * @param[in] ctx   to allocate a new #fr_socket_t struct in.
179
 * @param[in] proto   one of the IPPROTO_* macros, i.e. IPPROTO_TCP, IPPROTO_UDP
180
 * @param[in] ifindex   The interface to originate the packet from Pass <= 0 to
181
 *        indicate an unknown or unspecified interface.
182
 * @param[in] src_ipaddr  The source IP address of the packet, or source interface for
183
 *        packets to egress out of.
184
 * @param[in] src_port    The source port of the packet or the source
185
 * @param[in] dst_ipaddr  The destination IP address of the packet.
186
 * @param[in] dst_port    The destination port of the packet.
187
 * @return
188
 *  - NULL if invalid parameters are provided.
189
 *  - An initialised fr_socket_t struct.
190
 */
191
static inline fr_socket_t *fr_socket_addr_alloc_inet(TALLOC_CTX *ctx, int proto,
192
                 int ifindex, fr_ipaddr_t const *src_ipaddr, int src_port,
193
                 fr_ipaddr_t const *dst_ipaddr, int dst_port)
194
0
{
195
0
  FR_SOCKET_ADDR_ALLOC_DEF_FUNC(fr_socket_addr_init_inet,
196
0
              proto, ifindex, src_ipaddr, src_port, dst_ipaddr, dst_port)
197
0
}
Unexecuted instantiation: dl.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: packet.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: socket.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: udp.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: udp_queue.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: base.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: decode.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: encode.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: raw.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: list.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: tcp.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: abinary.c:fr_socket_addr_alloc_inet
Unexecuted instantiation: vmps.c:fr_socket_addr_alloc_inet
198
199
/** A variant of fr_socket_addr_alloc_inet will also allocates a #fr_socket_t
200
 *
201
202
 * @param[out] addr   to initialise.
203
 * @param[in] proto   one of the IPPROTO_* macros, i.e. IPPROTO_TCP, IPPROTO_UDP
204
 * @param[in] ifindex   The interface to originate the packet from Pass <= 0 to
205
 *        indicate an unknown or unspecified interface.
206
 * @param[in] ipaddr    The IP address to bind to.  May be all zeros to bind to
207
 *        all addresses, but the AF must still be specified.
208
 * @param[in] port    The source port to bind to.
209
 * @return
210
 *  - NULL if invalid parameters are provided.
211
 *  - An initialised fr_socket_t struct.
212
 */
213
static inline fr_socket_t *fr_socket_addr_init_inet_src(fr_socket_t *addr,
214
              int proto, int ifindex, fr_ipaddr_t const *ipaddr, int port)
215
0
{
216
0
  if (!fr_socket_proto_is_known(proto)) return NULL;
217
0
218
0
  *addr = (fr_socket_t){
219
0
    .af = ipaddr->af,
220
0
    .type = (proto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM,
221
0
    .inet = {
222
0
      .ifindex = ifindex,
223
0
      .src_ipaddr = *ipaddr,
224
0
      .src_port = port
225
0
    }
226
0
  };
227
0
228
0
  return addr;
229
0
}
Unexecuted instantiation: dl.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: packet.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: socket.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: udp.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: udp_queue.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: base.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: decode.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: encode.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: raw.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: list.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: tcp.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: abinary.c:fr_socket_addr_init_inet_src
Unexecuted instantiation: vmps.c:fr_socket_addr_init_inet_src
230
231
/** A variant of fr_socket_addr_init_inet_src will also allocates a #fr_socket_t
232
 *
233
 * @param[in] ctx   to allocate a new #fr_socket_t struct in.
234
 * @param[in] proto   one of the IPPROTO_* macros, i.e. IPPROTO_TCP, IPPROTO_UDP
235
 * @param[in] ifindex   The interface to originate the packet from Pass <= 0 to
236
 *        indicate an unknown or unspecified interface.
237
 * @param[in] ipaddr    The IP address to bind to.  May be all zeros to bind to
238
 *        all addresses, but the AF must still be specified.
239
 * @param[in] port    The source port to bind to.
240
 * @return
241
 *  - NULL if invalid parameters are provided.
242
 *  - An initialised fr_socket_t struct.
243
 */
244
static inline fr_socket_t *fr_socket_addr_alloc_inet_src(TALLOC_CTX *ctx, int proto,
245
               int ifindex, fr_ipaddr_t const *ipaddr, int port)
246
0
{
247
0
  FR_SOCKET_ADDR_ALLOC_DEF_FUNC(fr_socket_addr_init_inet_src, proto, ifindex, ipaddr, port)
248
0
}
Unexecuted instantiation: dl.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: packet.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: socket.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: udp.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: udp_queue.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: base.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: decode.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: encode.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: raw.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: list.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: tcp.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: abinary.c:fr_socket_addr_alloc_inet_src
Unexecuted instantiation: vmps.c:fr_socket_addr_alloc_inet_src
249
/** Initialise a #fr_socket_t for connecting to a remote host
250
 *
251
 * @param[out] addr   to initialise.
252
 * @param[in] proto   one of the IPPROTO_* macros, i.e. IPPROTO_TCP, IPPROTO_UDP
253
 * @param[in] ipaddr    The IP address to bind to.  May be all zeros to bind to
254
 *        all addresses, but the AF must still be specified.
255
 * @param[in] port    The source port to bind to.
256
 * @return
257
 *  - NULL if invalid parameters are provided.
258
 *  - An initialised fr_socket_t struct.
259
 */
260
static inline fr_socket_t *fr_socket_addr_init_inet_dst(fr_socket_t *addr, int proto, fr_ipaddr_t const *ipaddr, int port)
261
0
{
262
0
  if (!fr_socket_proto_is_known(proto)) return NULL;
263
0
264
0
  *addr = (fr_socket_t){
265
0
    .af = ipaddr->af,
266
0
    .type = (proto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM,
267
0
    .inet = {
268
0
      .dst_ipaddr = *ipaddr,
269
0
      .dst_port = port
270
0
    }
271
0
  };
272
0
273
0
  return addr;
274
0
}
Unexecuted instantiation: dl.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: packet.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: socket.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: udp.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: udp_queue.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: base.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: decode.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: encode.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: raw.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: list.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: tcp.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: abinary.c:fr_socket_addr_init_inet_dst
Unexecuted instantiation: vmps.c:fr_socket_addr_init_inet_dst
275
276
/** A variant of fr_socket_addr_alloc_inet_dst that will also allocates a #fr_socket_t
277
 *
278
 * @param[in] ctx   to allocate new #fr_socket_t struct in.
279
 * @param[in] proto   one of the IPPROTO_* macros, i.e. IPPROTO_TCP, IPPROTO_UDP
280
 * @param[in] ipaddr    The IP address to bind to.  May be all zeros to bind to
281
 *        all addresses, but the AF must still be specified.
282
 * @param[in] port    The source port to bind to.
283
 * @return
284
 *  - NULL if invalid parameters are provided.
285
 *  - An initialised fr_socket_t struct.
286
 */
287
static inline fr_socket_t *fr_socket_addr_alloc_inet_dst(TALLOC_CTX *ctx, int proto,
288
               fr_ipaddr_t const *ipaddr, int port)
289
0
{
290
0
  FR_SOCKET_ADDR_ALLOC_DEF_FUNC(fr_socket_addr_init_inet_dst, proto, ipaddr, port)
291
0
}
Unexecuted instantiation: dl.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: packet.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: socket.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: udp.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: udp_queue.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: base.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: decode.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: encode.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: raw.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: list.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: tcp.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: abinary.c:fr_socket_addr_alloc_inet_dst
Unexecuted instantiation: vmps.c:fr_socket_addr_alloc_inet_dst
292
293
int   fr_socket_client_unix(char const *path, bool async);
294
295
int   fr_socket_client_udp(char const *ifname, fr_ipaddr_t *src_ipaddr, uint16_t *src_port,
296
             fr_ipaddr_t const *dst_ipaddr, uint16_t dst_port, bool async);
297
298
int   fr_socket_client_tcp(char const *ifname, fr_ipaddr_t *src_ipaddr,
299
             fr_ipaddr_t const *dst_ipaddr, uint16_t dst_port, bool async);
300
int   fr_socket_wait_for_connect(int sockfd, fr_time_delta_t timeout);
301
302
int   fr_socket_server_udp(fr_ipaddr_t const *ipaddr, uint16_t *port, char const *port_name, bool async);
303
304
int   fr_socket_server_tcp(fr_ipaddr_t const *ipaddr, uint16_t *port, char const *port_name, bool async);
305
306
int   fr_socket_bind(int sockfd, char const *ifname, fr_ipaddr_t *src_ipaddr, uint16_t *src_port);
307
308
#ifdef __cplusplus
309
}
310
#endif