Coverage Report

Created: 2025-11-24 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/librabbitmq/librabbitmq/amqp_url.c
Line
Count
Source
1
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
2
// SPDX-License-Identifier: mit
3
4
#ifdef HAVE_CONFIG_H
5
#include "config.h"
6
#endif
7
8
#ifdef _MSC_VER
9
#define _CRT_SECURE_NO_WARNINGS
10
#endif
11
12
#include "amqp_private.h"
13
#include <limits.h>
14
#include <stdint.h>
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <string.h>
18
19
402
void amqp_default_connection_info(struct amqp_connection_info *ci) {
20
  /* Apply defaults */
21
402
  ci->user = "guest";
22
402
  ci->password = "guest";
23
402
  ci->host = "localhost";
24
402
  ci->port = 5672;
25
402
  ci->vhost = "/";
26
402
  ci->ssl = 0;
27
402
}
28
29
/* Scan for the next delimiter, handling percent-encodings on the way. */
30
568
static char find_delim(char **pp, int colon_and_at_sign_are_delims) {
31
568
  char *from = *pp;
32
568
  char *to = from;
33
34
3.22k
  for (;;) {
35
3.22k
    char ch = *from++;
36
37
3.22k
    switch (ch) {
38
478
      case ':':
39
779
      case '@':
40
779
        if (!colon_and_at_sign_are_delims) {
41
564
          *to++ = ch;
42
564
          break;
43
564
        }
44
45
      /* fall through */
46
470
      case 0:
47
479
      case '/':
48
482
      case '?':
49
485
      case '#':
50
530
      case '[':
51
542
      case ']':
52
542
        *to = 0;
53
542
        *pp = from;
54
542
        return ch;
55
56
231
      case '%': {
57
231
        unsigned int val;
58
231
        int chars;
59
231
        int res = sscanf(from, "%2x%n", &val, &chars);
60
61
231
        if (res == EOF || res < 1 || chars != 2 || val > CHAR_MAX)
62
        /* Return a surprising delimiter to
63
           force an error. */
64
26
        {
65
26
          return '%';
66
26
        }
67
68
205
        *to++ = (char)val;
69
205
        from += 2;
70
205
        break;
71
231
      }
72
73
1.88k
      default:
74
1.88k
        *to++ = ch;
75
1.88k
        break;
76
3.22k
    }
77
3.22k
  }
78
568
}
79
80
/* Parse an AMQP URL into its component parts. */
81
402
int amqp_parse_url(char *url, struct amqp_connection_info *parsed) {
82
402
  int res = AMQP_STATUS_BAD_URL;
83
402
  char delim;
84
402
  char *start;
85
402
  char *host;
86
402
  char *port = NULL;
87
88
402
  amqp_default_connection_info(parsed);
89
90
  /* check the prefix */
91
402
  if (!strncmp(url, "amqp://", 7)) {
92
    /* do nothing */
93
308
  } else if (!strncmp(url, "amqps://", 8)) {
94
1
    parsed->port = 5671;
95
1
    parsed->ssl = 1;
96
93
  } else {
97
93
    goto out;
98
93
  }
99
100
309
  host = start = url += (parsed->ssl ? 8 : 7);
101
309
  delim = find_delim(&url, 1);
102
103
309
  if (delim == ':') {
104
    /* The colon could be introducing the port or the
105
       password part of the userinfo.  We don't know yet,
106
       so stash the preceding component. */
107
192
    port = start = url;
108
192
    delim = find_delim(&url, 1);
109
192
  }
110
111
309
  if (delim == '@') {
112
    /* What might have been the host and port were in fact
113
       the username and password */
114
10
    parsed->user = host;
115
10
    if (port) {
116
5
      parsed->password = port;
117
5
    }
118
119
10
    port = NULL;
120
10
    host = start = url;
121
10
    delim = find_delim(&url, 1);
122
10
  }
123
124
309
  if (delim == '[') {
125
    /* IPv6 address.  The bracket should be the first
126
       character in the host. */
127
43
    if (host != start || *host != 0) {
128
9
      goto out;
129
9
    }
130
131
34
    start = url;
132
34
    delim = find_delim(&url, 0);
133
134
34
    if (delim != ']') {
135
24
      goto out;
136
24
    }
137
138
10
    parsed->host = start;
139
10
    start = url;
140
10
    delim = find_delim(&url, 1);
141
142
    /* Closing bracket should be the last character in the
143
       host. */
144
10
    if (*start != 0) {
145
8
      goto out;
146
8
    }
147
266
  } else {
148
    /* If we haven't seen the host yet, this is it. */
149
266
    if (*host != 0) {
150
61
      parsed->host = host;
151
61
    }
152
266
  }
153
154
268
  if (delim == ':') {
155
6
    port = url;
156
6
    delim = find_delim(&url, 1);
157
6
  }
158
159
268
  if (port) {
160
189
    char *end;
161
189
    long portnum = strtol(port, &end, 10);
162
163
189
    if (port == end || *end != 0 || portnum < 0 || portnum > 65535) {
164
160
      goto out;
165
160
    }
166
167
29
    parsed->port = portnum;
168
29
  }
169
170
108
  if (delim == '/') {
171
7
    start = url;
172
7
    delim = find_delim(&url, 1);
173
174
7
    if (delim != 0) {
175
6
      goto out;
176
6
    }
177
178
1
    parsed->vhost = start;
179
1
    res = AMQP_STATUS_OK;
180
101
  } else if (delim == 0) {
181
68
    res = AMQP_STATUS_OK;
182
68
  }
183
184
  /* Any other delimiter is bad, and we will return AMQP_STATUS_BAD_AMQP_URL. */
185
186
402
out:
187
402
  return res;
188
108
}