/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 | } |