]> git.ipfire.org Git - people/ms/libloc.git/blob - src/address.c
Refactor parsing IP addresses
[people/ms/libloc.git] / src / address.c
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>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <libloc/address.h>
25
26 #define LOC_ADDRESS_BUFFERS 6
27 #define LOC_ADDRESS_BUFFER_LENGTH INET6_ADDRSTRLEN
28
29 static char __loc_address_buffers[LOC_ADDRESS_BUFFERS][LOC_ADDRESS_BUFFER_LENGTH + 1];
30 static int __loc_address_buffer_idx = 0;
31
32 static const char* __loc_address6_str(const struct in6_addr* address, char* buffer, size_t length) {
33 return inet_ntop(AF_INET6, address, buffer, length);
34 }
35
36 static const char* __loc_address4_str(const struct in6_addr* address, char* buffer, size_t length) {
37 struct in_addr address4 = {
38 .s_addr = address->s6_addr32[3],
39 };
40
41 return inet_ntop(AF_INET, &address4, buffer, length);
42 }
43
44 const char* loc_address_str(const struct in6_addr* address) {
45 if (!address)
46 return NULL;
47
48 // Select buffer
49 char* buffer = __loc_address_buffers[__loc_address_buffer_idx++];
50
51 // Prevent index from overflow
52 __loc_address_buffer_idx %= LOC_ADDRESS_BUFFERS;
53
54 if (IN6_IS_ADDR_V4MAPPED(address))
55 return __loc_address4_str(address, buffer, LOC_ADDRESS_BUFFER_LENGTH);
56 else
57 return __loc_address6_str(address, buffer, LOC_ADDRESS_BUFFER_LENGTH);
58 }
59
60 static void loc_address_from_address4(struct in6_addr* address,
61 const struct in_addr* address4) {
62 address->s6_addr32[0] = 0;
63 address->s6_addr32[1] = 0;
64 address->s6_addr32[2] = htonl(0xffff);
65 address->s6_addr32[3] = address4->s_addr;
66 }
67
68 int loc_address_parse(struct in6_addr* address, unsigned int* prefix, const char* string) {
69 char buffer[INET6_ADDRSTRLEN + 4];
70 int r;
71
72 if (!address || !string) {
73 errno = EINVAL;
74 return 1;
75 }
76
77 // Copy the string into the buffer
78 r = snprintf(buffer, sizeof(buffer) - 1, "%s", string);
79 if (r < 0)
80 return 1;
81
82 // Find /
83 char* p = strchr(buffer, '/');
84 if (p) {
85 // Terminate the IP address
86 *p++ = '\0';
87 }
88
89 int family = AF_UNSPEC;
90
91 // Try parsing as an IPv6 address
92 r = inet_pton(AF_INET6, buffer, address);
93 switch (r) {
94 // This is not a valid IPv6 address
95 case 0:
96 break;
97
98 // This is a valid IPv6 address
99 case 1:
100 family = AF_INET6;
101 break;
102
103 // Unexpected error
104 default:
105 return 1;
106 }
107
108 // Try parsing as an IPv4 address
109 if (!family) {
110 struct in_addr address4;
111
112 r = inet_pton(AF_INET, buffer, &address4);
113 switch (r) {
114 // This was not a valid IPv4 address
115 case 0:
116 break;
117
118 // This was a valid IPv4 address
119 case 1:
120 family = AF_INET;
121
122 // Copy the result
123 loc_address_from_address4(address, &address4);
124 break;
125
126 // Unexpected error
127 default:
128 return 1;
129 }
130 }
131
132 // Invalid input
133 if (family == AF_UNSPEC) {
134 errno = EINVAL;
135 return 1;
136 }
137
138 // Did the user request a prefix?
139 if (prefix) {
140 // Set the prefix to the default value
141 *prefix = loc_address_family_bit_length(family);
142
143 // Parse the actual string
144 if (p)
145 *prefix = strtol(p, NULL, 10);
146 }
147
148 return 0;
149 }