Coverage Report

Created: 2026-07-14 06:05

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 non null terminated string into an integer.
29
 *
30
 * str: the string containing the number.
31
 * len: Number of characters to parse.
32
 */
33
static inline int
34
natoi(const char *str, size_t len)
35
0
{
36
0
  int i, r = 0;
37
0
  for (i = 0; i < len; i++) {
38
0
    r *= 10;
39
0
    r += str[i] - '0';
40
0
  }
41
42
0
  return r;
43
0
}
44
45
/**
46
 * Check if a URL is relative (no scheme and hostname).
47
 *
48
 * url: the string containing the URL to check.
49
 *
50
 * Returns 1 if relative, otherwise 0.
51
 */
52
static inline int
53
is_relative(const char *url)
54
0
{
55
0
  return (*url == '/') ? 1 : 0;
56
0
}
57
58
/**
59
 * Parse the scheme of a URL by inserting a null terminator after the scheme.
60
 *
61
 * str: the string containing the URL to parse. Will be modified.
62
 *
63
 * Returns a pointer to the hostname on success, otherwise NULL.
64
 */
65
static inline char *
66
parse_scheme(char *str)
67
0
{
68
0
  char *s;
69
70
  /* If not found or first in string, return error */
71
0
  s = strchr(str, ':');
72
0
  if (s == NULL || s == str) {
73
0
    return NULL;
74
0
  }
75
76
  /* If not followed by two slashes, return error */
77
0
  if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') {
78
0
    return NULL;
79
0
  }
80
81
0
  *s = '\0'; // Replace ':' with NULL
82
83
0
  return s + 3;
84
0
}
85
86
/**
87
 * Find a character in a string, replace it with '\0' and return the next
88
 * character in the string.
89
 *
90
 * str: the string to search in.
91
 * find: the character to search for.
92
 *
93
 * Returns a pointer to the character after the one to search for. If not
94
 * found, NULL is returned.
95
 */
96
static inline char *
97
find_and_terminate(char *str, char find)
98
0
{
99
0
  str = strchr(str, find);
100
0
  if (NULL == str) {
101
0
    return NULL;
102
0
  }
103
104
0
  *str = '\0';
105
0
  return str + 1;
106
0
}
107
108
/* Yes, the following functions could be implemented as preprocessor macros
109
     instead of inline functions, but I think that this approach will be more
110
     clean in this case. */
111
static inline char *
112
find_fragment(char *str)
113
0
{
114
0
  return find_and_terminate(str, '#');
115
0
}
116
117
static inline char *
118
find_query(char *str)
119
0
{
120
0
  return find_and_terminate(str, '?');
121
0
}
122
123
static inline char *
124
find_path(char *str)
125
0
{
126
0
  return find_and_terminate(str, '/');
127
0
}
128
129
/**
130
 * Parse a URL string to a struct.
131
 *
132
 * url: pointer to the struct where to store the parsed URL parts.
133
 * u:   the string containing the URL to be parsed.
134
 *
135
 * Returns 0 on success, otherwise -1.
136
 */
137
int
138
yuarel_parse(struct yuarel *url, char *u)
139
0
{
140
0
  if (NULL == url || NULL == u) {
141
0
    return -1;
142
0
  }
143
144
0
  memset(url, 0, sizeof (struct yuarel));
145
146
  /* (Fragment) */
147
0
  url->fragment = find_fragment(u);
148
149
  /* (Query) */
150
0
  url->query = find_query(u);
151
152
  /* Relative URL? Parse scheme and hostname */
153
0
  if (!is_relative(u)) {
154
    /* Scheme */
155
0
    url->scheme = u;
156
0
    u = parse_scheme(u);
157
0
    if (u == NULL) {
158
0
      return -1;
159
0
    }
160
161
    /* Host */
162
0
    if ('\0' == *u) {
163
0
      return -1;
164
0
    }
165
0
    url->host = u;
166
167
    /* (Path) */
168
0
    url->path = find_path(u);
169
170
    /* (Credentials) */
171
0
    u = strchr(url->host, '@');
172
0
    if (NULL != u) {
173
      /* Missing credentials? */
174
0
      if (u == url->host) {
175
0
        return -1;
176
0
      }
177
178
0
      url->username = url->host;
179
0
      url->host = u + 1;
180
0
      *u = '\0';
181
182
0
      u = strchr(url->username, ':');
183
0
      if (NULL == u) {
184
0
        return -1;
185
0
      }
186
187
0
      url->password = u + 1;
188
0
      *u = '\0';
189
0
    }
190
191
    /* Missing hostname? */
192
0
    if ('\0' == *url->host) {
193
0
      return -1;
194
0
    }
195
196
    /* (Port) */
197
        // If hostname starts with square bracket, it is IPv6 literal
198
    // example:
199
              // http://[1080:0:0:0:8:800:200C:417A]:80/index.html
200
              // http://[3ffe:2a00:100:7031::1]
201
              // http://[::192.9.5.5]/ipng
202
0
    if ('[' == url->host[0]) {
203
0
      u = strchr(url->host, ']');
204
0
      if (NULL != u) {
205
0
        url->host++;
206
0
                *(u++) = '\0';
207
0
        if ('\0' == *u) {
208
0
          u = NULL;
209
0
        } else if (':' != *u) {
210
0
          return -1;
211
0
        }
212
0
      } else {
213
0
        return -1;
214
0
      }
215
0
    } else {
216
0
      u = strchr(url->host, ':');
217
0
    }
218
0
    if (NULL != u && (NULL == url->path || u < url->path)) {
219
0
      *(u++) = '\0';
220
0
      if ('\0' == *u) {
221
0
        return -1;
222
0
      }
223
224
0
      if (url->path) {
225
0
        url->port = natoi(u, url->path - u - 1);
226
0
      } else {
227
0
        url->port = atoi(u);
228
0
      }
229
0
    }
230
231
    /* Missing hostname? */
232
0
    if ('\0' == *url->host) {
233
0
      return -1;
234
0
    }
235
0
  } else {
236
    /* (Path) */
237
0
    url->path = find_path(u);
238
0
  }
239
240
0
  return 0;
241
0
}
242
243
/**
244
 * Split a path into several strings.
245
 *
246
 * No data is copied, the slashed are used as null terminators and then
247
 * pointers to each path part will be stored in **parts. Double slashes will be
248
 * treated as one.
249
 *
250
 * path: the path to split.
251
 * parts: a pointer to an array of (char *) where to store the result.
252
 * max_parts: max number of parts to parse.
253
 */
254
int
255
yuarel_split_path(char *path, char **parts, int max_parts)
256
0
{
257
0
  int i = 0;
258
259
0
  if (NULL == path || '\0' == *path) {
260
0
    return -1;
261
0
  }
262
263
0
  do {
264
    /* Forward to after slashes */
265
0
    while (*path == '/') path++;
266
267
0
    if ('\0' == *path) {
268
0
      break;
269
0
    }
270
271
0
    parts[i++] = path;
272
273
0
    path = strchr(path, '/');
274
0
    if (NULL == path) {
275
0
      break;
276
0
    }
277
278
0
    *(path++) = '\0';
279
0
  } while (i < max_parts);
280
281
0
  return i;
282
0
}
283
284
int
285
yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params)
286
0
{
287
0
  int i = 0;
288
289
0
  if (NULL == query || '\0' == *query) {
290
0
    return -1;
291
0
  }
292
293
0
  params[i++].key = query;
294
0
  while (i < max_params && NULL != (query = strchr(query, delimiter))) {
295
0
    *query = '\0';
296
0
    params[i].key = ++query;
297
0
    params[i].val = NULL;
298
299
    /* Go back and split previous param */
300
0
    if (i > 0) {
301
0
      if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
302
0
        *(params[i - 1].val)++ = '\0';
303
0
      }
304
0
    }
305
0
    i++;
306
0
  }
307
308
  /* Go back and split last param */
309
0
  if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
310
0
    *(params[i - 1].val)++ = '\0';
311
0
  }
312
313
0
  return i;
314
0
}