]> git.ipfire.org Git - location/libloc.git/blame - src/address.c
Make sources around that we can run tests without location installed
[location/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>
25
26#define LOC_ADDRESS_BUFFERS 6
27#define LOC_ADDRESS_BUFFER_LENGTH INET6_ADDRSTRLEN
28
b0f572df 29static char __loc_address_buffers[LOC_ADDRESS_BUFFERS][LOC_ADDRESS_BUFFER_LENGTH + 1];
0a0a289a
MT
30static int __loc_address_buffer_idx = 0;
31
32static 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
36static 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
44const 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}
95b6a8e4
MT
59
60static 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
68int 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
17e3d8fd 141 const unsigned int max_prefix = loc_address_family_bit_length(family);
95b6a8e4
MT
142
143 // Parse the actual string
17e3d8fd 144 if (p) {
95b6a8e4 145 *prefix = strtol(p, NULL, 10);
17e3d8fd
MT
146
147 // Check if prefix is within bounds
148 if (*prefix > max_prefix) {
149 errno = EINVAL;
150 return 1;
151 }
152
153 // If the string didn't contain a prefix, we set the maximum
154 } else {
155 *prefix = max_prefix;
156 }
95b6a8e4
MT
157 }
158
159 return 0;
160}