]> git.ipfire.org Git - people/ms/libloc.git/blame - src/address.c
importer: Drop EDROP as it has been merged into DROP
[people/ms/libloc.git] / src / address.c
CommitLineData
0a0a289a
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2022 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
17#include <arpa/inet.h>
18#include <netinet/in.h>
19#include <stddef.h>
95b6a8e4
MT
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
0a0a289a
MT
23
24#include <libloc/address.h>
25f300f7 25#include <libloc/compat.h>
0a0a289a
MT
26
27#define LOC_ADDRESS_BUFFERS 6
28#define LOC_ADDRESS_BUFFER_LENGTH INET6_ADDRSTRLEN
29
b0f572df 30static char __loc_address_buffers[LOC_ADDRESS_BUFFERS][LOC_ADDRESS_BUFFER_LENGTH + 1];
0a0a289a
MT
31static int __loc_address_buffer_idx = 0;
32
33static const char* __loc_address6_str(const struct in6_addr* address, char* buffer, size_t length) {
34 return inet_ntop(AF_INET6, address, buffer, length);
35}
36
37static const char* __loc_address4_str(const struct in6_addr* address, char* buffer, size_t length) {
38 struct in_addr address4 = {
39 .s_addr = address->s6_addr32[3],
40 };
41
42 return inet_ntop(AF_INET, &address4, buffer, length);
43}
44
45const char* loc_address_str(const struct in6_addr* address) {
46 if (!address)
47 return NULL;
48
49 // Select buffer
50 char* buffer = __loc_address_buffers[__loc_address_buffer_idx++];
51
52 // Prevent index from overflow
53 __loc_address_buffer_idx %= LOC_ADDRESS_BUFFERS;
54
55 if (IN6_IS_ADDR_V4MAPPED(address))
56 return __loc_address4_str(address, buffer, LOC_ADDRESS_BUFFER_LENGTH);
57 else
58 return __loc_address6_str(address, buffer, LOC_ADDRESS_BUFFER_LENGTH);
59}
95b6a8e4
MT
60
61static void loc_address_from_address4(struct in6_addr* address,
62 const struct in_addr* address4) {
63 address->s6_addr32[0] = 0;
64 address->s6_addr32[1] = 0;
65 address->s6_addr32[2] = htonl(0xffff);
66 address->s6_addr32[3] = address4->s_addr;
67}
68
69int loc_address_parse(struct in6_addr* address, unsigned int* prefix, const char* string) {
70 char buffer[INET6_ADDRSTRLEN + 4];
71 int r;
72
73 if (!address || !string) {
74 errno = EINVAL;
75 return 1;
76 }
77
78 // Copy the string into the buffer
79 r = snprintf(buffer, sizeof(buffer) - 1, "%s", string);
80 if (r < 0)
81 return 1;
82
83 // Find /
84 char* p = strchr(buffer, '/');
85 if (p) {
86 // Terminate the IP address
87 *p++ = '\0';
88 }
89
90 int family = AF_UNSPEC;
91
92 // Try parsing as an IPv6 address
93 r = inet_pton(AF_INET6, buffer, address);
94 switch (r) {
95 // This is not a valid IPv6 address
96 case 0:
97 break;
98
99 // This is a valid IPv6 address
100 case 1:
101 family = AF_INET6;
102 break;
103
104 // Unexpected error
105 default:
106 return 1;
107 }
108
109 // Try parsing as an IPv4 address
110 if (!family) {
111 struct in_addr address4;
112
113 r = inet_pton(AF_INET, buffer, &address4);
114 switch (r) {
115 // This was not a valid IPv4 address
116 case 0:
117 break;
118
119 // This was a valid IPv4 address
120 case 1:
121 family = AF_INET;
122
123 // Copy the result
124 loc_address_from_address4(address, &address4);
125 break;
126
127 // Unexpected error
128 default:
129 return 1;
130 }
131 }
132
133 // Invalid input
134 if (family == AF_UNSPEC) {
135 errno = EINVAL;
136 return 1;
137 }
138
139 // Did the user request a prefix?
140 if (prefix) {
141 // Set the prefix to the default value
17e3d8fd 142 const unsigned int max_prefix = loc_address_family_bit_length(family);
95b6a8e4
MT
143
144 // Parse the actual string
17e3d8fd 145 if (p) {
95b6a8e4 146 *prefix = strtol(p, NULL, 10);
17e3d8fd
MT
147
148 // Check if prefix is within bounds
149 if (*prefix > max_prefix) {
150 errno = EINVAL;
151 return 1;
152 }
153
154 // If the string didn't contain a prefix, we set the maximum
155 } else {
156 *prefix = max_prefix;
157 }
95b6a8e4
MT
158 }
159
160 return 0;
161}