Coverage Report

Created: 2026-11-06 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}