Coverage Report

Created: 2025-07-12 06:02

/src/librabbitmq/librabbitmq/amqp_url.c
Line
Count
Source (jump to first uncovered line)
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
399
void amqp_default_connection_info(struct amqp_connection_info *ci) {
20
  /* Apply defaults */
21
399
  ci->user = "guest";
22
399
  ci->password = "guest";
23
399
  ci->host = "localhost";
24
399
  ci->port = 5672;
25
399
  ci->vhost = "/";
26
399
  ci->ssl = 0;
27
399
}
28
29
/* Scan for the next delimiter, handling percent-encodings on the way. */
30
570
static char find_delim(char **pp, int colon_and_at_sign_are_delims) {
31
570
  char *from = *pp;
32
570
  char *to = from;
33
34
3.20k
  for (;;) {
35
3.20k
    char ch = *from++;
36
37
3.20k
    switch (ch) {
38
466
      case ':':
39
755
      case '@':
40
755
        if (!colon_and_at_sign_are_delims) {
41
537
          *to++ = ch;
42
537
          break;
43
537
        }
44
45
      /* fall through */
46
472
      case 0:
47
481
      case '/':
48
484
      case '?':
49
487
      case '#':
50
531
      case '[':
51
544
      case ']':
52
544
        *to = 0;
53
544
        *pp = from;
54
544
        return ch;
55
56
229
      case '%': {
57
229
        unsigned int val;
58
229
        int chars;
59
229
        int res = sscanf(from, "%2x%n", &val, &chars);
60
61
229
        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
203
        *to++ = (char)val;
69
203
        from += 2;
70
203
        break;
71
229
      }
72
73
1.89k
      default:
74
1.89k
        *to++ = ch;
75
1.89k
        break;
76
3.20k
    }
77
3.20k
  }
78
570
}
79
80
/* Parse an AMQP URL into its component parts. */
81
399
int amqp_parse_url(char *url, struct amqp_connection_info *parsed) {
82
399
  int res = AMQP_STATUS_BAD_URL;
83
399
  char delim;
84
399
  char *start;
85
399
  char *host;
86
399
  char *port = NULL;
87
88
399
  amqp_default_connection_info(parsed);
89
90
  /* check the prefix */
91
399
  if (!strncmp(url, "amqp://", 7)) {
92
    /* do nothing */
93
306
  } else if (!strncmp(url, "amqps://", 8)) {
94
1
    parsed->port = 5671;
95
1
    parsed->ssl = 1;
96
92
  } else {
97
92
    goto out;
98
92
  }
99
100
307
  host = start = url += (parsed->ssl ? 8 : 7);
101
307
  delim = find_delim(&url, 1);
102
103
307
  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
193
    port = start = url;
108
193
    delim = find_delim(&url, 1);
109
193
  }
110
111
307
  if (delim == '@') {
112
    /* What might have been the host and port were in fact
113
       the username and password */
114
12
    parsed->user = host;
115
12
    if (port) {
116
4
      parsed->password = port;
117
4
    }
118
119
12
    port = NULL;
120
12
    host = start = url;
121
12
    delim = find_delim(&url, 1);
122
12
  }
123
124
307
  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
23
      goto out;
136
23
    }
137
138
11
    parsed->host = start;
139
11
    start = url;
140
11
    delim = find_delim(&url, 1);
141
142
    /* Closing bracket should be the last character in the
143
       host. */
144
11
    if (*start != 0) {
145
8
      goto out;
146
8
    }
147
264
  } else {
148
    /* If we haven't seen the host yet, this is it. */
149
264
    if (*host != 0) {
150
57
      parsed->host = host;
151
57
    }
152
264
  }
153
154
267
  if (delim == ':') {
155
6
    port = url;
156
6
    delim = find_delim(&url, 1);
157
6
  }
158
159
267
  if (port) {
160
191
    char *end;
161
191
    long portnum = strtol(port, &end, 10);
162
163
191
    if (port == end || *end != 0 || portnum < 0 || portnum > 65535) {
164
159
      goto out;
165
159
    }
166
167
32
    parsed->port = portnum;
168
32
  }
169
170
108
  if (delim == '/') {
171
7
    start = url;
172
7
    delim = find_delim(&url, 1);
173
174
7
    if (delim != 0) {
175
5
      goto out;
176
5
    }
177
178
2
    parsed->vhost = start;
179
2
    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
399
out:
187
399
  return res;
188
108
}