/src/open5gs/lib/sbi/yuarel.c
Line | Count | Source |
1 | | /** |
2 | | * Copyright (C) 2016,2017 Jack Engqvist Johansson |
3 | | * |
4 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | | * of this software and associated documentation files (the "Software"), to deal |
6 | | * in the Software without restriction, including without limitation the rights |
7 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
8 | | * copies of the Software, and to permit persons to whom the Software is |
9 | | * furnished to do so, subject to the following conditions: |
10 | | * |
11 | | * The above copyright notice and this permission notice shall be included in all |
12 | | * copies or substantial portions of the Software. |
13 | | * |
14 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
19 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
20 | | * SOFTWARE. |
21 | | */ |
22 | | #include <stdlib.h> |
23 | | #include <stdio.h> |
24 | | #include <string.h> |
25 | | #include "yuarel.h" |
26 | | |
27 | | /** |
28 | | * Parse a null terminated port string. |
29 | | * |
30 | | * Returns the port on success, otherwise -1. |
31 | | */ |
32 | | static inline int |
33 | | parse_port(const char *str) |
34 | 55 | { |
35 | 55 | int r = 0; |
36 | | |
37 | 55 | if ('\0' == *str) { |
38 | 3 | return -1; |
39 | 3 | } |
40 | | |
41 | 367 | while (*str) { |
42 | 343 | int digit; |
43 | | |
44 | 343 | if (*str < '0' || *str > '9') { |
45 | 12 | return -1; |
46 | 12 | } |
47 | 331 | digit = *str++ - '0'; |
48 | 331 | if (r > (65535 - digit) / 10) { |
49 | 16 | return -1; |
50 | 16 | } |
51 | 315 | r = r * 10 + digit; |
52 | 315 | } |
53 | | |
54 | 24 | return r; |
55 | 52 | } |
56 | | |
57 | | /** |
58 | | * Check if a URL is relative (no scheme and hostname). |
59 | | * |
60 | | * url: the string containing the URL to check. |
61 | | * |
62 | | * Returns 1 if relative, otherwise 0. |
63 | | */ |
64 | | static inline int |
65 | | is_relative(const char *url) |
66 | 602 | { |
67 | 602 | return (*url == '/') ? 1 : 0; |
68 | 602 | } |
69 | | |
70 | | /** |
71 | | * Parse the scheme of a URL by inserting a null terminator after the scheme. |
72 | | * |
73 | | * str: the string containing the URL to parse. Will be modified. |
74 | | * |
75 | | * Returns a pointer to the hostname on success, otherwise NULL. |
76 | | */ |
77 | | static inline char * |
78 | | parse_scheme(char *str) |
79 | 602 | { |
80 | 602 | char *s; |
81 | | |
82 | | /* If not found or first in string, return error */ |
83 | 602 | s = strchr(str, ':'); |
84 | 602 | if (s == NULL || s == str) { |
85 | 258 | return NULL; |
86 | 258 | } |
87 | | |
88 | | /* If not followed by two slashes, return error */ |
89 | 344 | if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') { |
90 | 32 | return NULL; |
91 | 32 | } |
92 | | |
93 | 312 | *s = '\0'; // Replace ':' with NULL |
94 | | |
95 | 312 | return s + 3; |
96 | 344 | } |
97 | | |
98 | | /** |
99 | | * Find a character in a string, replace it with '\0' and return the next |
100 | | * character in the string. |
101 | | * |
102 | | * str: the string to search in. |
103 | | * find: the character to search for. |
104 | | * |
105 | | * Returns a pointer to the character after the one to search for. If not |
106 | | * found, NULL is returned. |
107 | | */ |
108 | | static inline char * |
109 | | find_and_terminate(char *str, char find) |
110 | 1.51k | { |
111 | 1.51k | str = strchr(str, find); |
112 | 1.51k | if (NULL == str) { |
113 | 1.11k | return NULL; |
114 | 1.11k | } |
115 | | |
116 | 403 | *str = '\0'; |
117 | 403 | return str + 1; |
118 | 1.51k | } |
119 | | |
120 | | /* Yes, the following functions could be implemented as preprocessor macros |
121 | | instead of inline functions, but I think that this approach will be more |
122 | | clean in this case. */ |
123 | | static inline char * |
124 | | find_fragment(char *str) |
125 | 602 | { |
126 | 602 | return find_and_terminate(str, '#'); |
127 | 602 | } |
128 | | |
129 | | static inline char * |
130 | | find_query(char *str) |
131 | 602 | { |
132 | 602 | return find_and_terminate(str, '?'); |
133 | 602 | } |
134 | | |
135 | | static inline char * |
136 | | find_path(char *str) |
137 | 311 | { |
138 | 311 | return find_and_terminate(str, '/'); |
139 | 311 | } |
140 | | |
141 | | /** |
142 | | * Parse a URL string to a struct. |
143 | | * |
144 | | * url: pointer to the struct where to store the parsed URL parts. |
145 | | * u: the string containing the URL to be parsed. |
146 | | * |
147 | | * Returns 0 on success, otherwise -1. |
148 | | */ |
149 | | int |
150 | | yuarel_parse(struct yuarel *url, char *u) |
151 | 602 | { |
152 | 602 | if (NULL == url || NULL == u) { |
153 | 0 | return -1; |
154 | 0 | } |
155 | | |
156 | 602 | memset(url, 0, sizeof (struct yuarel)); |
157 | | |
158 | | /* (Fragment) */ |
159 | 602 | url->fragment = find_fragment(u); |
160 | | |
161 | | /* (Query) */ |
162 | 602 | url->query = find_query(u); |
163 | | |
164 | | /* Relative URL? Parse scheme and hostname */ |
165 | 602 | if (!is_relative(u)) { |
166 | | /* Scheme */ |
167 | 602 | url->scheme = u; |
168 | 602 | u = parse_scheme(u); |
169 | 602 | if (u == NULL) { |
170 | 290 | return -1; |
171 | 290 | } |
172 | | |
173 | | /* Host */ |
174 | 312 | if ('\0' == *u) { |
175 | 1 | return -1; |
176 | 1 | } |
177 | 311 | url->host = u; |
178 | | |
179 | | /* (Path) */ |
180 | 311 | url->path = find_path(u); |
181 | | |
182 | | /* (Credentials) */ |
183 | 311 | u = strchr(url->host, '@'); |
184 | 311 | if (NULL != u) { |
185 | | /* Missing credentials? */ |
186 | 3 | if (u == url->host) { |
187 | 1 | return -1; |
188 | 1 | } |
189 | | |
190 | 2 | url->username = url->host; |
191 | 2 | url->host = u + 1; |
192 | 2 | *u = '\0'; |
193 | | |
194 | 2 | u = strchr(url->username, ':'); |
195 | 2 | if (NULL == u) { |
196 | 1 | return -1; |
197 | 1 | } |
198 | | |
199 | 1 | url->password = u + 1; |
200 | 1 | *u = '\0'; |
201 | 1 | } |
202 | | |
203 | | /* Missing hostname? */ |
204 | 309 | if ('\0' == *url->host) { |
205 | 2 | return -1; |
206 | 2 | } |
207 | | |
208 | | /* (Port) */ |
209 | | // If hostname starts with square bracket, it is IPv6 literal |
210 | | // example: |
211 | | // http://[1080:0:0:0:8:800:200C:417A]:80/index.html |
212 | | // http://[3ffe:2a00:100:7031::1] |
213 | | // http://[::192.9.5.5]/ipng |
214 | 307 | if ('[' == url->host[0]) { |
215 | 16 | u = strchr(url->host, ']'); |
216 | 16 | if (NULL != u) { |
217 | 15 | url->host++; |
218 | 15 | *(u++) = '\0'; |
219 | 15 | if ('\0' == *u) { |
220 | 2 | u = NULL; |
221 | 13 | } else if (':' != *u) { |
222 | 11 | return -1; |
223 | 11 | } |
224 | 15 | } else { |
225 | 1 | return -1; |
226 | 1 | } |
227 | 291 | } else { |
228 | 291 | u = strchr(url->host, ':'); |
229 | 291 | } |
230 | 295 | if (NULL != u && (NULL == url->path || u < url->path)) { |
231 | 55 | *(u++) = '\0'; |
232 | 55 | url->port = parse_port(u); |
233 | 55 | if (url->port < 0) { |
234 | 31 | return -1; |
235 | 31 | } |
236 | 55 | } |
237 | | |
238 | | /* Missing hostname? */ |
239 | 264 | if ('\0' == *url->host) { |
240 | 18 | return -1; |
241 | 18 | } |
242 | 264 | } else { |
243 | | /* (Path) */ |
244 | 0 | url->path = find_path(u); |
245 | 0 | } |
246 | | |
247 | 246 | return 0; |
248 | 602 | } |
249 | | |
250 | | /** |
251 | | * Split a path into several strings. |
252 | | * |
253 | | * No data is copied, the slashed are used as null terminators and then |
254 | | * pointers to each path part will be stored in **parts. Double slashes will be |
255 | | * treated as one. |
256 | | * |
257 | | * path: the path to split. |
258 | | * parts: a pointer to an array of (char *) where to store the result. |
259 | | * max_parts: max number of parts to parse. |
260 | | */ |
261 | | int |
262 | | yuarel_split_path(char *path, char **parts, int max_parts) |
263 | 0 | { |
264 | 0 | int i = 0; |
265 | |
|
266 | 0 | if (NULL == path || '\0' == *path) { |
267 | 0 | return -1; |
268 | 0 | } |
269 | | |
270 | 0 | do { |
271 | | /* Forward to after slashes */ |
272 | 0 | while (*path == '/') path++; |
273 | |
|
274 | 0 | if ('\0' == *path) { |
275 | 0 | break; |
276 | 0 | } |
277 | | |
278 | 0 | parts[i++] = path; |
279 | |
|
280 | 0 | path = strchr(path, '/'); |
281 | 0 | if (NULL == path) { |
282 | 0 | break; |
283 | 0 | } |
284 | | |
285 | 0 | *(path++) = '\0'; |
286 | 0 | } while (i < max_parts); |
287 | |
|
288 | 0 | return i; |
289 | 0 | } |
290 | | |
291 | | int |
292 | | yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params) |
293 | 1.99k | { |
294 | 1.99k | int i = 0; |
295 | | |
296 | 1.99k | if (NULL == query || '\0' == *query) { |
297 | 0 | return -1; |
298 | 0 | } |
299 | | |
300 | 1.99k | params[i++].key = query; |
301 | 2.86k | while (i < max_params && NULL != (query = strchr(query, delimiter))) { |
302 | 867 | *query = '\0'; |
303 | 867 | params[i].key = ++query; |
304 | 867 | params[i].val = NULL; |
305 | | |
306 | | /* Go back and split previous param */ |
307 | 867 | if (i > 0) { |
308 | 867 | if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) { |
309 | 817 | *(params[i - 1].val)++ = '\0'; |
310 | 817 | } |
311 | 867 | } |
312 | 867 | i++; |
313 | 867 | } |
314 | | |
315 | | /* Go back and split last param */ |
316 | 1.99k | if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) { |
317 | 1.78k | *(params[i - 1].val)++ = '\0'; |
318 | 1.78k | } |
319 | | |
320 | 1.99k | return i; |
321 | 1.99k | } |