Coverage Report

Created: 2025-12-14 06:42

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
396
void amqp_default_connection_info(struct amqp_connection_info *ci) {
20
  /* Apply defaults */
21
396
  ci->user = "guest";
22
396
  ci->password = "guest";
23
396
  ci->host = "localhost";
24
396
  ci->port = 5672;
25
396
  ci->vhost = "/";
26
396
  ci->ssl = 0;
27
396
}
28
29
/* Scan for the next delimiter, handling percent-encodings on the way. */
30
556
static char find_delim(char **pp, int colon_and_at_sign_are_delims) {
31
556
  char *from = *pp;
32
556
  char *to = from;
33
34
3.19k
  for (;;) {
35
3.19k
    char ch = *from++;
36
37
3.19k
    switch (ch) {
38
467
      case ':':
39
745
      case '@':
40
745
        if (!colon_and_at_sign_are_delims) {
41
535
          *to++ = ch;
42
535
          break;
43
535
        }
44
45
      /* fall through */
46
460
      case 0:
47
469
      case '/':
48
472
      case '?':
49
475
      case '#':
50
519
      case '[':
51
531
      case ']':
52
531
        *to = 0;
53
531
        *pp = from;
54
531
        return ch;
55
56
228
      case '%': {
57
228
        unsigned int val;
58
228
        int chars;
59
228
        int res = sscanf(from, "%2x%n", &val, &chars);
60
61
228
        if (res == EOF || res < 1 || chars != 2 || val > CHAR_MAX)
62
        /* Return a surprising delimiter to
63
           force an error. */
64
25
        {
65
25
          return '%';
66
25
        }
67
68
203
        *to++ = (char)val;
69
203
        from += 2;
70
203
        break;
71
228
      }
72
73
1.90k
      default:
74
1.90k
        *to++ = ch;
75
1.90k
        break;
76
3.19k
    }
77
3.19k
  }
78
556
}
79
80
/* Parse an AMQP URL into its component parts. */
81
396
int amqp_parse_url(char *url, struct amqp_connection_info *parsed) {
82
396
  int res = AMQP_STATUS_BAD_URL;
83
396
  char delim;
84
396
  char *start;
85
396
  char *host;
86
396
  char *port = NULL;
87
88
396
  amqp_default_connection_info(parsed);
89
90
  /* check the prefix */
91
396
  if (!strncmp(url, "amqp://", 7)) {
92
    /* do nothing */
93
301
  } else if (!strncmp(url, "amqps://", 8)) {
94
1
    parsed->port = 5671;
95
1
    parsed->ssl = 1;
96
94
  } else {
97
94
    goto out;
98
94
  }
99
100
302
  host = start = url += (parsed->ssl ? 8 : 7);
101
302
  delim = find_delim(&url, 1);
102
103
302
  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
190
    port = start = url;
108
190
    delim = find_delim(&url, 1);
109
190
  }
110
111
302
  if (delim == '@') {
112
    /* What might have been the host and port were in fact
113
       the username and password */
114
9
    parsed->user = host;
115
9
    if (port) {
116
4
      parsed->password = port;
117
4
    }
118
119
9
    port = NULL;
120
9
    host = start = url;
121
9
    delim = find_delim(&url, 1);
122
9
  }
123
124
302
  if (delim == '[') {
125
    /* IPv6 address.  The bracket should be the first
126
       character in the host. */
127
42
    if (host != start || *host != 0) {
128
9
      goto out;
129
9
    }
130
131
33
    start = url;
132
33
    delim = find_delim(&url, 0);
133
134
33
    if (delim != ']') {
135
23
      goto out;
136
23
    }
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
260
  } else {
148
    /* If we haven't seen the host yet, this is it. */
149
260
    if (*host != 0) {
150
58
      parsed->host = host;
151
58
    }
152
260
  }
153
154
262
  if (delim == ':') {
155
5
    port = url;
156
5
    delim = find_delim(&url, 1);
157
5
  }
158
159
262
  if (port) {
160
187
    char *end;
161
187
    long portnum = strtol(port, &end, 10);
162
163
187
    if (port == end || *end != 0 || portnum < 0 || portnum > 65535) {
164
157
      goto out;
165
157
    }
166
167
30
    parsed->port = portnum;
168
30
  }
169
170
105
  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
98
  } else if (delim == 0) {
181
67
    res = AMQP_STATUS_OK;
182
67
  }
183
184
  /* Any other delimiter is bad, and we will return AMQP_STATUS_BAD_AMQP_URL. */
185
186
396
out:
187
396
  return res;
188
105
}