]> git.ipfire.org Git - people/ms/libloc.git/blob - src/address.c
lua: Add AS object
[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 #include <libloc/compat.h>
26
27 #define LOC_ADDRESS_BUFFERS 6
28 #define LOC_ADDRESS_BUFFER_LENGTH INET6_ADDRSTRLEN
29
30 static char __loc_address_buffers[LOC_ADDRESS_BUFFERS][LOC_ADDRESS_BUFFER_LENGTH + 1];
31 static int __loc_address_buffer_idx = 0;
32
33 static 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
37 static 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
45 const 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 }
60
61 static 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
69 int 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
142 const unsigned int max_prefix = loc_address_family_bit_length(family);
143
144 // Parse the actual string
145 if (p) {
146 *prefix = strtol(p, NULL, 10);
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 }
158 }
159
160 return 0;
161 }