blob: c9d9a946c43cb5fe13e09daf7c1b49ca3c6ff1eb [file] [log] [blame]
Neels Hofmeyr0c7826e2019-02-25 02:45:06 +01001/*! \file sockaddr_str.c
2 * Common implementation to store an IP address and port.
3 */
4/*
5 * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
6 *
7 * Author: neels@hofmeyr.de
8 *
9 * All Rights Reserved
10 *
11 * SPDX-License-Identifier: GPL-2.0+
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 */
28
29#include "config.h"
30
31#ifdef HAVE_NETINET_IN_H
32#include <string.h>
33#include <errno.h>
34#include <netinet/in.h>
35#include <arpa/inet.h>
36
37#include <osmocom/core/sockaddr_str.h>
38#include <osmocom/core/utils.h>
39#include <osmocom/core/byteswap.h>
40
41/*! \addtogroup sockaddr_str
42 *
43 * Common operations to store IP address as a char string along with a uint16_t port number.
44 *
45 * Convert IP address string to/from in_addr and in6_addr, with bounds checking and basic housekeeping.
46 *
47 * The initial purpose is to store and translate IP address info between GSM CC and MGCP protocols -- GSM mostly using
48 * 32-bit IPv4 addresses, and MGCP forwarding addresses as ASCII character strings.
49 *
50 * (At the time of writing, there are no immediate IPv6 users that come to mind, but it seemed appropriate to
51 * accommodate both address families from the start.)
52 *
53 * @{
54 * \file sockaddr_str.c
55 */
56
57/*! Return true if all elements of the osmo_sockaddr_str instance are set.
58 * \param[in] sockaddr_str The instance to examine.
59 * \return True iff ip is nonempty, port is not 0 and af is set to either AF_INET or AF_INET6.
60 */
61bool osmo_sockaddr_str_is_set(const struct osmo_sockaddr_str *sockaddr_str)
62{
63 return *sockaddr_str->ip
64 && sockaddr_str->port
65 && (sockaddr_str->af == AF_INET || sockaddr_str->af == AF_INET6);
66}
67
68/*! Distinguish between valid IPv4 and IPv6 strings.
69 * This does not verify whether the string is a valid IP address; it assumes that the input is a valid IP address, and
70 * on that premise returns whether it is an IPv4 or IPv6 string, by looking for '.' and ':' characters. It is safe to
71 * feed invalid address strings, but the return value is only guaranteed to be meaningful if the input was valid.
72 * \param[in] ip Valid IP address string.
73 * \return AF_INET or AF_INET6, or AF_UNSPEC if neither '.' nor ':' are found in the string.
74 */
75int osmo_ip_str_type(const char *ip)
76{
77 if (!ip)
78 return AF_UNSPEC;
79 /* Could also be IPv4-mapped IPv6 format with both colons and dots: x:x:x:x:x:x:d.d.d.d */
80 if (strchr(ip, ':'))
81 return AF_INET6;
82 if (strchr(ip, '.'))
83 return AF_INET;
84 return AF_UNSPEC;
85}
86
87/*! Safely copy the given ip string to sockaddr_str, classify to AF_INET or AF_INET6, and set the port.
88 * Data will be written to sockaddr_str even if an error is returned.
89 * \param[out] sockaddr_str The instance to copy to.
90 * \param[in] ip Valid IP address string.
91 * \param[in] port Port number.
92 * \return 0 on success, negative if copying the address string failed (e.g. too long), if the address family could
93 * not be detected (i.e. if osmo_ip_str_type() returned AF_UNSPEC), or if sockaddr_str is NULL.
94 */
95int osmo_sockaddr_str_from_str(struct osmo_sockaddr_str *sockaddr_str, const char *ip, uint16_t port)
96{
97 int rc;
98 if (!sockaddr_str)
99 return -ENOSPC;
100 if (!ip)
101 ip = "";
102 *sockaddr_str = (struct osmo_sockaddr_str){
103 .af = osmo_ip_str_type(ip),
104 .port = port,
105 };
106 rc = osmo_strlcpy(sockaddr_str->ip, ip, sizeof(sockaddr_str->ip));
107 if (rc <= 0)
108 return -EIO;
109 if (rc >= sizeof(sockaddr_str->ip))
110 return -ENOSPC;
111 if (sockaddr_str->af == AF_UNSPEC)
112 return -EINVAL;
113 return 0;
114}
115
116/*! Convert IPv4 address to osmo_sockaddr_str, and set port.
117 * \param[out] sockaddr_str The instance to copy to.
118 * \param[in] addr IPv4 address data.
119 * \param[in] port Port number.
120 * \return 0 on success, negative on error.
121 */
122int osmo_sockaddr_str_from_in_addr(struct osmo_sockaddr_str *sockaddr_str, const struct in_addr *addr, uint16_t port)
123{
124 if (!sockaddr_str)
125 return -ENOSPC;
126 *sockaddr_str = (struct osmo_sockaddr_str){
127 .af = AF_INET,
128 .port = port,
129 };
130 if (!inet_ntop(AF_INET, addr, sockaddr_str->ip, sizeof(sockaddr_str->ip)))
131 return -ENOSPC;
132 return 0;
133}
134
135/*! Convert IPv6 address to osmo_sockaddr_str, and set port.
136 * \param[out] sockaddr_str The instance to copy to.
137 * \param[in] addr IPv6 address data.
138 * \param[in] port Port number.
139 * \return 0 on success, negative on error.
140 */
141int osmo_sockaddr_str_from_in6_addr(struct osmo_sockaddr_str *sockaddr_str, const struct in6_addr *addr, uint16_t port)
142{
143 if (!sockaddr_str)
144 return -ENOSPC;
145 *sockaddr_str = (struct osmo_sockaddr_str){
146 .af = AF_INET6,
147 .port = port,
148 };
149 if (!inet_ntop(AF_INET6, addr, sockaddr_str->ip, sizeof(sockaddr_str->ip)))
150 return -ENOSPC;
151 return 0;
152}
153
154/*! Convert IPv4 address from 32bit host-byte-order to osmo_sockaddr_str, and set port.
155 * \param[out] sockaddr_str The instance to copy to.
156 * \param[in] addr 32bit IPv4 address data.
157 * \param[in] port Port number.
158 * \return 0 on success, negative on error.
159 */
160int osmo_sockaddr_str_from_32(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port)
161{
162 struct in_addr addr;
163 if (!sockaddr_str)
164 return -ENOSPC;
165 addr.s_addr = ip;
166 return osmo_sockaddr_str_from_in_addr(sockaddr_str, &addr, port);
167}
168
169/*! Convert IPv4 address from 32bit network-byte-order to osmo_sockaddr_str, and set port.
170 * \param[out] sockaddr_str The instance to copy to.
171 * \param[in] addr 32bit IPv4 address data.
172 * \param[in] port Port number.
173 * \return 0 on success, negative on error.
174 */
175int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port)
176{
177 if (!sockaddr_str)
178 return -ENOSPC;
179 return osmo_sockaddr_str_from_32(sockaddr_str, osmo_ntohl(ip), port);
180}
181
182/*! Convert IPv4 address and port to osmo_sockaddr_str.
183 * \param[out] sockaddr_str The instance to copy to.
184 * \param[in] src IPv4 address and port data.
185 * \return 0 on success, negative on error.
186 */
187int osmo_sockaddr_str_from_sockaddr_in(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in *src)
188{
189 if (!sockaddr_str)
190 return -ENOSPC;
191 if (!src)
192 return -EINVAL;
193 if (src->sin_family != AF_INET)
194 return -EINVAL;
195 return osmo_sockaddr_str_from_in_addr(sockaddr_str, &src->sin_addr, osmo_ntohs(src->sin_port));
196}
197
198/*! Convert IPv6 address and port to osmo_sockaddr_str.
199 * \param[out] sockaddr_str The instance to copy to.
200 * \param[in] src IPv6 address and port data.
201 * \return 0 on success, negative on error.
202 */
203int osmo_sockaddr_str_from_sockaddr_in6(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in6 *src)
204{
205 if (!sockaddr_str)
206 return -ENOSPC;
207 if (!src)
208 return -EINVAL;
209 if (src->sin6_family != AF_INET6)
210 return -EINVAL;
211 return osmo_sockaddr_str_from_in6_addr(sockaddr_str, &src->sin6_addr, osmo_ntohs(src->sin6_port));
212}
213
214/*! Convert IPv4 or IPv6 address and port to osmo_sockaddr_str.
215 * \param[out] sockaddr_str The instance to copy to.
216 * \param[in] src IPv4 or IPv6 address and port data.
217 * \return 0 on success, negative if src does not indicate AF_INET nor AF_INET6 (or if the conversion fails, which
218 * should not be possible in practice).
219 */
220int osmo_sockaddr_str_from_sockaddr(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_storage *src)
221{
222 const struct sockaddr_in *sin = (void*)src;
223 const struct sockaddr_in6 *sin6 = (void*)src;
224 if (!sockaddr_str)
225 return -ENOSPC;
226 if (!src)
227 return -EINVAL;
228 if (sin->sin_family == AF_INET)
229 return osmo_sockaddr_str_from_sockaddr_in(sockaddr_str, sin);
230 if (sin6->sin6_family == AF_INET6)
231 return osmo_sockaddr_str_from_sockaddr_in6(sockaddr_str, sin6);
232 return -EINVAL;
233}
234
235/*! Convert osmo_sockaddr_str address string to IPv4 address data.
236 * \param[in] sockaddr_str The instance to convert the IP of.
237 * \param[out] dst IPv4 address data to write to.
238 * \return 0 on success, negative on error (e.g. invalid IPv4 address string).
239 */
240int osmo_sockaddr_str_to_in_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in_addr *dst)
241{
242 int rc;
243 if (!sockaddr_str)
244 return -EINVAL;
245 if (!dst)
246 return -ENOSPC;
247 if (sockaddr_str->af != AF_INET)
248 return -EAFNOSUPPORT;
249 rc = inet_pton(AF_INET, sockaddr_str->ip, dst);
250 if (rc != 1)
251 return -EINVAL;
252 return 0;
253}
254
255/*! Convert osmo_sockaddr_str address string to IPv6 address data.
256 * \param[in] sockaddr_str The instance to convert the IP of.
257 * \param[out] dst IPv6 address data to write to.
258 * \return 0 on success, negative on error (e.g. invalid IPv6 address string).
259 */
260int osmo_sockaddr_str_to_in6_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in6_addr *dst)
261{
262 int rc;
263 if (!sockaddr_str)
264 return -EINVAL;
265 if (!dst)
266 return -ENOSPC;
267 if (sockaddr_str->af != AF_INET6)
268 return -EINVAL;
269 rc = inet_pton(AF_INET6, sockaddr_str->ip, dst);
270 if (rc != 1)
271 return -EINVAL;
272 return 0;
273}
274
275/*! Convert osmo_sockaddr_str address string to IPv4 address data in host-byte-order.
276 * \param[in] sockaddr_str The instance to convert the IP of.
277 * \param[out] dst IPv4 address data in 32bit host-byte-order format to write to.
278 * \return 0 on success, negative on error (e.g. invalid IPv4 address string).
279 */
280int osmo_sockaddr_str_to_32(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip)
281{
282 int rc;
283 struct in_addr addr;
284 if (!sockaddr_str)
285 return -EINVAL;
286 if (!ip)
287 return -ENOSPC;
288 rc = osmo_sockaddr_str_to_in_addr(sockaddr_str, &addr);
289 if (rc)
290 return rc;
291 *ip = addr.s_addr;
292 return 0;
293}
294
295/*! Convert osmo_sockaddr_str address string to IPv4 address data in network-byte-order.
296 * \param[in] sockaddr_str The instance to convert the IP of.
297 * \param[out] dst IPv4 address data in 32bit network-byte-order format to write to.
298 * \return 0 on success, negative on error (e.g. invalid IPv4 address string).
299 */
300int osmo_sockaddr_str_to_32n(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip)
301{
302 int rc;
303 uint32_t ip_h;
304 if (!sockaddr_str)
305 return -EINVAL;
306 if (!ip)
307 return -ENOSPC;
308 rc = osmo_sockaddr_str_to_32(sockaddr_str, &ip_h);
309 if (rc)
310 return rc;
311 *ip = osmo_htonl(ip_h);
312 return 0;
313}
314
315/*! Convert osmo_sockaddr_str address string and port to IPv4 address and port data.
316 * \param[in] sockaddr_str The instance to convert the IP and port of.
317 * \param[out] dst IPv4 address and port data to write to.
318 * \return 0 on success, negative on error (e.g. invalid IPv4 address string).
319 */
320int osmo_sockaddr_str_to_sockaddr_in(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in *dst)
321{
322 if (!sockaddr_str)
323 return -EINVAL;
324 if (!dst)
325 return -ENOSPC;
326 if (sockaddr_str->af != AF_INET)
327 return -EINVAL;
328 *dst = (struct sockaddr_in){
329 .sin_family = sockaddr_str->af,
330 .sin_port = osmo_htons(sockaddr_str->port),
331 };
332 return osmo_sockaddr_str_to_in_addr(sockaddr_str, &dst->sin_addr);
333}
334
335/*! Convert osmo_sockaddr_str address string and port to IPv6 address and port data.
336 * \param[in] sockaddr_str The instance to convert the IP and port of.
337 * \param[out] dst IPv6 address and port data to write to.
338 * \return 0 on success, negative on error (e.g. invalid IPv6 address string).
339 */
340int osmo_sockaddr_str_to_sockaddr_in6(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in6 *dst)
341{
342 if (!sockaddr_str)
343 return -EINVAL;
344 if (!dst)
345 return -ENOSPC;
346 if (sockaddr_str->af != AF_INET6)
347 return -EINVAL;
348 *dst = (struct sockaddr_in6){
349 .sin6_family = sockaddr_str->af,
350 .sin6_port = osmo_htons(sockaddr_str->port),
351 };
352 return osmo_sockaddr_str_to_in6_addr(sockaddr_str, &dst->sin6_addr);
353}
354
355/*! Convert osmo_sockaddr_str address string and port to IPv4 or IPv6 address and port data.
356 * Depending on sockaddr_str->af, dst will be handled as struct sockaddr_in or struct sockaddr_in6.
357 * \param[in] sockaddr_str The instance to convert the IP and port of.
358 * \param[out] dst IPv4/IPv6 address and port data to write to.
359 * \return 0 on success, negative on error (e.g. invalid IP address string for the family indicated by sockaddr_str->af).
360 */
361int osmo_sockaddr_str_to_sockaddr(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_storage *dst)
362{
363 if (!sockaddr_str)
364 return -EINVAL;
365 if (!dst)
366 return -ENOSPC;
367 switch (sockaddr_str->af) {
368 case AF_INET:
369 return osmo_sockaddr_str_to_sockaddr_in(sockaddr_str, (void*)dst);
370 case AF_INET6:
371 return osmo_sockaddr_str_to_sockaddr_in6(sockaddr_str, (void*)dst);
372 default:
373 return -EINVAL;
374 }
375}
376
377/*! @} */
378#endif // HAVE_NETINET_IN_H