Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2001-2005 iptel.org |
3 | | * Copyright (C) 2007-2008 1&1 Internet AG |
4 | | * |
5 | | * This file is part of opensips, a free SIP server. |
6 | | * |
7 | | * opensips is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2 of the License, or |
10 | | * (at your option) any later version |
11 | | * |
12 | | * opensips is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | */ |
21 | | |
22 | | /** |
23 | | * \file db/db_id.c |
24 | | * \brief Functions for parsing a database URL and work with db identifier. |
25 | | */ |
26 | | |
27 | | #include "db_id.h" |
28 | | #include "../dprint.h" |
29 | | #include "../mem/mem.h" |
30 | | #include "../ut.h" |
31 | | #include <stdlib.h> |
32 | | #include <string.h> |
33 | | |
34 | | |
35 | | /** |
36 | | * Duplicate a string |
37 | | * \param dst destination |
38 | | * \param begin start of the string |
39 | | * \param end end of the string |
40 | | */ |
41 | | static int dupl_string(char** dst, const char* begin, const char* end) |
42 | 0 | { |
43 | 0 | str old, new; |
44 | |
|
45 | 0 | if (*dst) pkg_free(*dst); |
46 | |
|
47 | 0 | *dst = pkg_malloc(end - begin + 1); |
48 | 0 | if ((*dst) == NULL) { |
49 | 0 | return -1; |
50 | 0 | } |
51 | | |
52 | 0 | old.s = (char*)begin; |
53 | 0 | old.len = end - begin; |
54 | 0 | new.s = *dst; |
55 | 0 | un_escape(&old, &new ); |
56 | |
|
57 | 0 | new.s[new.len] = '\0'; |
58 | 0 | return 0; |
59 | 0 | } |
60 | | |
61 | | |
62 | | /** |
63 | | * Parse a database URL of form |
64 | | * scheme://[username[:password]@]hostname[:port]/database[?parameters] |
65 | | * |
66 | | * \param id filled id struct |
67 | | * \param url parsed URL |
68 | | * \return 0 if parsing was successful and -1 otherwise |
69 | | */ |
70 | | static int parse_db_url(struct db_id* id, const str* url) |
71 | 0 | { |
72 | 0 | #define SHORTEST_DB_URL "s://a/b" |
73 | 0 | #define SHORTEST_DB_URL_LEN (sizeof(SHORTEST_DB_URL) - 1) |
74 | |
|
75 | 0 | enum state { |
76 | 0 | ST_SCHEME, /* Scheme part */ |
77 | 0 | ST_SLASH1, /* First slash */ |
78 | 0 | ST_SLASH2, /* Second slash */ |
79 | 0 | ST_USER_HOST, /* Username or hostname */ |
80 | 0 | ST_PASS_PORT, /* Password or port part */ |
81 | 0 | ST_PASSWORD, /* Explicitly the password */ |
82 | 0 | ST_HOST, /* Hostname part */ |
83 | 0 | ST_HOST6, /* Hostname part IPv6 */ |
84 | 0 | ST_PORT, /* Port part */ |
85 | 0 | ST_UNIX_SOCKET, /* Unix socket */ |
86 | 0 | ST_DB, /* Database part */ |
87 | 0 | ST_PARAMS /* Parameters part */ |
88 | 0 | }; |
89 | |
|
90 | 0 | enum state st; |
91 | 0 | unsigned int len, i, ipv6_flag = 0; |
92 | 0 | const char* begin; |
93 | 0 | char* prev_token = NULL, *p; |
94 | 0 | str unix_socket_host = str_init("localhost"); |
95 | 0 | int have_pass = 0; |
96 | |
|
97 | 0 | if (!id || !url || !url->s) { |
98 | 0 | return -1; |
99 | 0 | } |
100 | | |
101 | 0 | len = url->len; |
102 | 0 | if (len < SHORTEST_DB_URL_LEN) { |
103 | 0 | return -1; |
104 | 0 | } |
105 | | |
106 | | /* Initialize all attributes to 0 */ |
107 | 0 | memset(id, 0, sizeof(struct db_id)); |
108 | 0 | st = ST_SCHEME; |
109 | 0 | begin = url->s; |
110 | | |
111 | | /* to allow support for '/' characters in password => look ahead! */ |
112 | 0 | p = q_memchr(url->s, ':', len); |
113 | 0 | if (p && q_memchr(p, '@', len - (p - url->s))) |
114 | 0 | have_pass = 1; |
115 | |
|
116 | 0 | for(i = 0; i < len; i++) { |
117 | 0 | switch(st) { |
118 | 0 | case ST_SCHEME: |
119 | 0 | switch(url->s[i]) { |
120 | 0 | case ':': |
121 | 0 | st = ST_SLASH1; |
122 | 0 | if (dupl_string(&id->scheme, begin, url->s + i) < 0) goto err; |
123 | 0 | break; |
124 | 0 | } |
125 | 0 | break; |
126 | | |
127 | 0 | case ST_SLASH1: |
128 | 0 | switch(url->s[i]) { |
129 | 0 | case '/': |
130 | 0 | st = ST_SLASH2; |
131 | 0 | break; |
132 | | |
133 | 0 | default: |
134 | 0 | goto err; |
135 | 0 | } |
136 | 0 | break; |
137 | | |
138 | 0 | case ST_SLASH2: |
139 | 0 | switch(url->s[i]) { |
140 | 0 | case '/': |
141 | 0 | st = ST_USER_HOST; |
142 | 0 | begin = url->s + i + 1; |
143 | 0 | break; |
144 | | |
145 | 0 | default: |
146 | 0 | goto err; |
147 | 0 | } |
148 | 0 | break; |
149 | | |
150 | 0 | case ST_USER_HOST: |
151 | 0 | switch(url->s[i]) { |
152 | 0 | case '@': |
153 | 0 | st = ST_HOST; |
154 | 0 | if (dupl_string(&id->username, begin, url->s + i) < 0) goto err; |
155 | 0 | begin = url->s + i + 1; |
156 | 0 | break; |
157 | | |
158 | 0 | case ':': |
159 | 0 | if (have_pass) |
160 | 0 | st = ST_PASSWORD; |
161 | 0 | else |
162 | 0 | st = ST_PORT; |
163 | 0 | if (dupl_string(&prev_token, begin, url->s + i) < 0) goto err; |
164 | 0 | begin = url->s + i + 1; |
165 | 0 | break; |
166 | | |
167 | 0 | case '[': |
168 | 0 | st = ST_HOST6; |
169 | 0 | begin = url->s + i + 1; |
170 | 0 | break; |
171 | | |
172 | 0 | case '/': |
173 | 0 | st = ST_DB; |
174 | 0 | if (dupl_string(&id->host, begin, url->s + i) < 0) goto err; |
175 | 0 | begin = url->s + i + 1; |
176 | 0 | } |
177 | 0 | break; |
178 | | |
179 | 0 | case ST_PASS_PORT: |
180 | 0 | switch(url->s[i]) { |
181 | 0 | case '@': |
182 | 0 | st = ST_HOST; |
183 | 0 | id->username = prev_token; prev_token = NULL; |
184 | 0 | if (dupl_string(&id->password, begin, url->s + i) < 0) goto err; |
185 | 0 | begin = url->s + i + 1; |
186 | 0 | break; |
187 | | |
188 | 0 | case '/': |
189 | 0 | id->host = prev_token; prev_token = NULL; |
190 | 0 | id->port = str2s(begin, url->s + i - begin, 0); |
191 | 0 | st = ST_DB; |
192 | 0 | begin = url->s + i + 1; |
193 | 0 | break; |
194 | 0 | } |
195 | 0 | break; |
196 | | |
197 | 0 | case ST_PASSWORD: |
198 | 0 | switch (url->s[i]) { |
199 | 0 | case '@': // Only @ terminates password |
200 | 0 | st = ST_HOST; |
201 | 0 | id->username = prev_token; prev_token = NULL; |
202 | 0 | if (dupl_string(&id->password, begin, url->s + i) < 0) goto err; |
203 | 0 | begin = url->s + i + 1; |
204 | 0 | break; |
205 | 0 | } |
206 | 0 | break; |
207 | | |
208 | 0 | case ST_HOST: |
209 | 0 | if (strncasecmp(begin, "unix(", 5) == 0) { |
210 | 0 | st = ST_UNIX_SOCKET; |
211 | 0 | i+=5; |
212 | 0 | begin = url->s + i; |
213 | 0 | break; |
214 | 0 | } |
215 | 0 | switch(url->s[i]) { |
216 | 0 | case '[': |
217 | 0 | st = ST_HOST6; |
218 | 0 | begin = url->s + i + 1; |
219 | 0 | break; |
220 | | |
221 | 0 | case ':': |
222 | 0 | st = ST_PORT; |
223 | 0 | if (dupl_string(&id->host, begin, url->s + i - ipv6_flag) < 0) goto err; |
224 | 0 | begin = url->s + i + 1; |
225 | 0 | break; |
226 | | |
227 | 0 | case '/': |
228 | 0 | if (dupl_string(&id->host, begin, url->s + i - ipv6_flag) < 0) goto err; |
229 | 0 | st = ST_DB; |
230 | 0 | begin = url->s + i + 1; |
231 | 0 | } |
232 | 0 | break; |
233 | | |
234 | 0 | case ST_HOST6: |
235 | 0 | switch(url->s[i]) { |
236 | 0 | case ']': |
237 | 0 | ipv6_flag = 1; |
238 | 0 | st = ST_HOST; |
239 | 0 | break; |
240 | |
|
241 | 0 | } |
242 | 0 | break; |
243 | | |
244 | 0 | case ST_UNIX_SOCKET: |
245 | 0 | switch(url->s[i]) { |
246 | 0 | case ')': |
247 | 0 | if (dupl_string(&id->unix_socket, begin, url->s + i) < 0) goto err; |
248 | 0 | if (dupl_string(&id->host, unix_socket_host.s, unix_socket_host.s + unix_socket_host.len) < 0) goto err; |
249 | 0 | begin = url->s + i + 1; |
250 | 0 | if (*begin == '/') { |
251 | 0 | i++; |
252 | 0 | begin = url->s + i + 1; |
253 | 0 | } |
254 | 0 | st = ST_DB; |
255 | 0 | } |
256 | 0 | break; |
257 | | |
258 | 0 | case ST_PORT: |
259 | 0 | switch(url->s[i]) { |
260 | 0 | case '/': |
261 | 0 | if (!have_pass) { |
262 | 0 | id->host = prev_token; |
263 | 0 | prev_token = NULL; |
264 | 0 | } |
265 | 0 | id->port = str2s(begin, url->s + i - begin, 0); |
266 | 0 | st = ST_DB; |
267 | 0 | begin = url->s + i + 1; |
268 | 0 | } |
269 | 0 | break; |
270 | | |
271 | 0 | case ST_DB: |
272 | 0 | switch(url->s[i]) { |
273 | 0 | case '?': |
274 | 0 | st = ST_PARAMS; |
275 | 0 | if (dupl_string(&id->database, begin, url->s + i) < 0) goto err; |
276 | 0 | begin = url->s + i + 1; |
277 | 0 | } |
278 | 0 | break; |
279 | | |
280 | 0 | case ST_PARAMS: |
281 | 0 | break; |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | 0 | if (st != ST_DB && st != ST_PARAMS) goto err; |
286 | | |
287 | 0 | if (st == ST_DB) { |
288 | 0 | if (dupl_string(&id->database, begin, url->s + len) < 0) goto err; |
289 | 0 | } else { |
290 | 0 | if (dupl_string(&id->parameters, begin, url->s + len) < 0) goto err; |
291 | 0 | } |
292 | | |
293 | 0 | return 0; |
294 | | |
295 | 0 | err: |
296 | 0 | if (id->scheme) pkg_free(id->scheme); |
297 | 0 | if (id->username) pkg_free(id->username); |
298 | 0 | if (id->password) pkg_free(id->password); |
299 | 0 | if (id->host) pkg_free(id->host); |
300 | 0 | if (id->unix_socket) pkg_free(id->unix_socket); |
301 | 0 | if (id->database) pkg_free(id->database); |
302 | 0 | if (prev_token) pkg_free(prev_token); |
303 | 0 | return -1; |
304 | 0 | } |
305 | | |
306 | | |
307 | | /** |
308 | | * Create a new connection identifier |
309 | | * \param url database URL |
310 | | * \return connection identifier, or zero on error |
311 | | */ |
312 | | struct db_id* new_db_id(const str* url) |
313 | 0 | { |
314 | 0 | struct db_id* ptr; |
315 | |
|
316 | 0 | if (!url || !url->s) { |
317 | 0 | LM_ERR("invalid parameter\n"); |
318 | 0 | return 0; |
319 | 0 | } |
320 | | |
321 | 0 | ptr = (struct db_id*)pkg_malloc(sizeof(struct db_id)); |
322 | 0 | if (!ptr) { |
323 | 0 | LM_ERR("no private memory left\n"); |
324 | 0 | goto err; |
325 | 0 | } |
326 | 0 | memset(ptr, 0, sizeof(struct db_id)); |
327 | |
|
328 | 0 | if (parse_db_url(ptr, url) < 0) { |
329 | 0 | LM_ERR("error while parsing database URL: '%.*s' \n", url->len, url->s); |
330 | 0 | goto err; |
331 | 0 | } |
332 | | |
333 | | /* store the original url */ |
334 | 0 | ptr->url.s = url->s; |
335 | 0 | ptr->url.len = url->len; |
336 | |
|
337 | 0 | return ptr; |
338 | | |
339 | 0 | err: |
340 | 0 | if (ptr) pkg_free(ptr); |
341 | 0 | return 0; |
342 | 0 | } |
343 | | |
344 | | |
345 | | /** |
346 | | * Compare two connection identifiers |
347 | | * \param id1 first identifier |
348 | | * \param id2 second identifier |
349 | | * \return one if both are equal, zero otherwise |
350 | | */ |
351 | | unsigned char cmp_db_id(const struct db_id* id1, const struct db_id* id2) |
352 | 0 | { |
353 | 0 | if (!id1 || !id2) return 0; |
354 | | |
355 | 0 | if (id1->port != id2->port) return 0; |
356 | | |
357 | 0 | if (strcmp(id1->scheme, id2->scheme)) return 0; |
358 | | |
359 | 0 | if (id1->username != 0 && id2->username != 0) { |
360 | 0 | if (strcmp(id1->username, id2->username)) return 0; |
361 | 0 | } else { |
362 | 0 | if (id1->username!=0 || id2->username!=0) return 0; |
363 | 0 | } |
364 | | |
365 | 0 | if (id1->password!=0 && id2->password!=0) { |
366 | 0 | if(strcmp(id1->password, id2->password)) return 0; |
367 | 0 | } else { |
368 | 0 | if (id1->password!=0 || id2->password!=0) return 0; |
369 | 0 | } |
370 | | |
371 | 0 | if (strcasecmp(id1->host, id2->host)) return 0; |
372 | | |
373 | 0 | if (id1->unix_socket!=0 && id2->unix_socket!=0) { |
374 | 0 | if (strcasecmp(id1->unix_socket, id2->unix_socket)) return 0; |
375 | 0 | } else { |
376 | 0 | if (id1->unix_socket!=0 || id2->unix_socket!=0) return 0; |
377 | 0 | } |
378 | | |
379 | 0 | if (strcmp(id1->database, id2->database)) return 0; |
380 | | |
381 | 0 | if (id1->parameters != 0 && id2->parameters != 0) { |
382 | 0 | if(strcmp(id1->parameters, id2->parameters)) return 0; |
383 | 0 | } else { |
384 | 0 | if (id1->parameters!=0 || id2->parameters!=0) return 0; |
385 | 0 | } |
386 | | |
387 | 0 | return 1; |
388 | 0 | } |
389 | | |
390 | | |
391 | | /** |
392 | | * Free a connection identifier |
393 | | * \param id identifier |
394 | | */ |
395 | | void free_db_id(struct db_id* id) |
396 | 0 | { |
397 | 0 | if (!id) return; |
398 | | |
399 | 0 | if (id->scheme) pkg_free(id->scheme); |
400 | 0 | if (id->username) pkg_free(id->username); |
401 | 0 | if (id->password) pkg_free(id->password); |
402 | 0 | if (id->host) pkg_free(id->host); |
403 | 0 | if (id->unix_socket) pkg_free(id->unix_socket); |
404 | 0 | if (id->database) pkg_free(id->database); |
405 | 0 | if (id->parameters) pkg_free(id->parameters); |
406 | 0 | pkg_free(id); |
407 | 0 | } |