1 /*
2 * PgBouncer - Lightweight connection pooler for PostgreSQL.
3 *
4 * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Random small utility functions
21 */
22
23 #include "bouncer.h"
24
25 #include <usual/crypto/md5.h>
26 #include <usual/crypto/csrandom.h>
27
log_socket_prefix(enum LogLevel lev,void * ctx,char * dst,unsigned int dstlen)28 int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstlen)
29 {
30 const struct PgSocket *sock = ctx;
31 const char *user, *db, *host;
32 char host6[PGADDR_BUF];
33 int port;
34 char stype;
35
36 /* no prefix */
37 if (!sock)
38 return 0;
39
40 /* format prefix */
41 stype = is_server_socket(sock) ? 'S' : 'C';
42 port = pga_port(&sock->remote_addr);
43 db = sock->pool ? sock->pool->db->name : "(nodb)";
44 user = sock->login_user ? sock->login_user->name : "(nouser)";
45 if (pga_is_unix(&sock->remote_addr)) {
46 unsigned long pid = sock->remote_addr.scred.pid;
47 if (pid) {
48 snprintf(host6, sizeof(host6), "unix(%lu)", pid);
49 host = host6;
50 } else {
51 host = "unix";
52 }
53 } else {
54 host = pga_ntop(&sock->remote_addr, host6, sizeof(host6));
55 }
56
57 if (pga_family(&sock->remote_addr) == AF_INET6) {
58 return snprintf(dst, dstlen, "%c-%p: %s/%s@[%s]:%d ",
59 stype, sock, db, user, host, port);
60 } else {
61 return snprintf(dst, dstlen, "%c-%p: %s/%s@%s:%d ",
62 stype, sock, db, user, host, port);
63 }
64 }
65
bin2hex(const uint8_t * src,unsigned srclen,char * dst,unsigned dstlen)66 const char *bin2hex(const uint8_t *src, unsigned srclen, char *dst, unsigned dstlen)
67 {
68 unsigned int i, j;
69 static const char hextbl [] = "0123456789abcdef";
70 if (!dstlen)
71 return "";
72 if (srclen*2+1 > dstlen)
73 srclen = (dstlen - 1) / 2;
74 for (i = j = 0; i < srclen; i++) {
75 dst[j++] = hextbl[src[i] >> 4];
76 dst[j++] = hextbl[src[i] & 15];
77 }
78 dst[j] = 0;
79 return dst;
80 }
81
82 /*
83 * PostgreSQL MD5 hashing.
84 */
85
hash2hex(const uint8_t * hash,char * dst)86 static void hash2hex(const uint8_t *hash, char *dst)
87 {
88 bin2hex(hash, MD5_DIGEST_LENGTH, dst, 16*2+1);
89 }
90
pg_md5_encrypt(const char * part1,const char * part2,size_t part2len,char * dest)91 void pg_md5_encrypt(const char *part1,
92 const char *part2, size_t part2len,
93 char *dest)
94 {
95 struct md5_ctx ctx;
96 uint8_t hash[MD5_DIGEST_LENGTH];
97
98 md5_reset(&ctx);
99 md5_update(&ctx, part1, strlen(part1));
100 md5_update(&ctx, part2, part2len);
101 md5_final(&ctx, hash);
102
103 memcpy(dest, "md5", 3);
104 hash2hex(hash, dest + 3);
105 }
106
107 /* wrapped for getting random bytes */
get_random_bytes(uint8_t * dest,int len)108 void get_random_bytes(uint8_t *dest, int len)
109 {
110 csrandom_bytes(dest, len);
111 }
112
113 /* set needed socket options */
tune_socket(int sock,bool is_unix)114 bool tune_socket(int sock, bool is_unix)
115 {
116 int res;
117 int val;
118 const char *errpos;
119 bool ok;
120
121 /*
122 * Generic stuff + nonblock.
123 */
124 errpos = "socket_setup";
125 ok = socket_setup(sock, true);
126 if (!ok)
127 goto fail;
128
129 /*
130 * Following options are for network sockets
131 */
132 if (is_unix)
133 return true;
134
135 /*
136 * TCP Keepalive
137 */
138 errpos = "socket_set_keepalive";
139 ok = socket_set_keepalive(sock, cf_tcp_keepalive, cf_tcp_keepidle,
140 cf_tcp_keepintvl, cf_tcp_keepcnt);
141 if (!ok)
142 goto fail;
143
144 /*
145 * TCP user timeout
146 */
147 if (cf_tcp_user_timeout) {
148 errpos = "setsockopt/TCP_USER_TIMEOUT";
149 #ifdef TCP_USER_TIMEOUT
150 val = cf_tcp_user_timeout;
151 res = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &val, sizeof(val));
152 if (res < 0)
153 goto fail;
154 #else
155 errno = EINVAL;
156 goto fail;
157 #endif
158 }
159
160 /*
161 * set in-kernel socket buffer size
162 */
163 if (cf_tcp_socket_buffer) {
164 val = cf_tcp_socket_buffer;
165 errpos = "setsockopt/SO_SNDBUF";
166 res = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val));
167 if (res < 0)
168 goto fail;
169 val = cf_tcp_socket_buffer;
170 errpos = "setsockopt/SO_RCVBUF";
171 res = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val));
172 if (res < 0)
173 goto fail;
174 }
175
176 /*
177 * Turn off kernel buffering, each send() will be one packet.
178 */
179 val = 1;
180 errpos = "setsockopt/TCP_NODELAY";
181 res = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
182 if (res < 0)
183 goto fail;
184 return true;
185 fail:
186 log_warning("%s(%d) failed: %s", errpos, sock, strerror(errno));
187 return false;
188 }
189
190 /*
191 * Find a string in comma-separated list.
192 *
193 * It does not support space inside tokens.
194 */
strlist_contains(const char * liststr,const char * str)195 bool strlist_contains(const char *liststr, const char *str)
196 {
197 int c, len = strlen(str);
198 const char *p, *listpos = liststr;
199
200 loop:
201 /* find string fragment, later check if actual token */
202 p = strstr(listpos, str);
203 if (p == NULL)
204 return false;
205
206 /* move listpos further */
207 listpos = p + len;
208 /* survive len=0 and avoid unnecessary compare */
209 if (*listpos)
210 listpos++;
211
212 /* check previous symbol */
213 if (p > liststr) {
214 c = *(p - 1);
215 if (!isspace(c) && c != ',')
216 goto loop;
217 }
218
219 /* check following symbol */
220 c = p[len];
221 if (c != 0 && !isspace(c) && c != ',')
222 goto loop;
223
224 return true;
225 }
226
fill_remote_addr(PgSocket * sk,int fd,bool is_unix)227 void fill_remote_addr(PgSocket *sk, int fd, bool is_unix)
228 {
229 PgAddr *dst = &sk->remote_addr;
230 socklen_t len = sizeof(PgAddr);
231 int err;
232
233 if (is_unix) {
234 uid_t uid = 0;
235 gid_t gid = 0;
236 pid_t pid = 0;
237 pga_set(dst, AF_UNIX, cf_listen_port);
238 if (getpeercreds(fd, &uid, &gid, &pid) >= 0) {
239 log_noise("unix peer uid: %d", (int)uid);
240 } else if (errno != ENOSYS) {
241 /*
242 * Check for ENOSYS, so we don't write a
243 * warning every time if the OS doesn't
244 * support this call.
245 */
246 log_warning("unix peer uid failed: %s", strerror(errno));
247 }
248 dst->scred.uid = uid;
249 dst->scred.pid = pid;
250 } else {
251 err = getpeername(fd, (struct sockaddr *)dst, &len);
252 if (err < 0) {
253 log_error("fill_remote_addr: getpeername(%d) = %s",
254 fd, strerror(errno));
255 }
256 }
257 }
258
fill_local_addr(PgSocket * sk,int fd,bool is_unix)259 void fill_local_addr(PgSocket *sk, int fd, bool is_unix)
260 {
261 PgAddr *dst = &sk->local_addr;
262 socklen_t len = sizeof(PgAddr);
263 int err;
264
265 if (is_unix) {
266 pga_set(dst, AF_UNIX, cf_listen_port);
267 dst->scred.uid = geteuid();
268 dst->scred.pid = getpid();
269 } else {
270 err = getsockname(fd, (struct sockaddr *)dst, &len);
271 if (err < 0) {
272 log_error("fill_local_addr: getsockname(%d) = %s",
273 fd, strerror(errno));
274 }
275 }
276 }
277
278 /*
279 * Error handling around evtimer_add() is nasty as the code
280 * may not be called again. As there is fixed number of timers
281 * in pgbouncer, provider safe_evtimer_add() that stores args of
282 * failed calls in static array and retries later.
283 */
284 #define TIMER_BACKUP_SLOTS 10
285
286 struct timer_slot {
287 struct event *ev;
288 struct timeval tv;
289 };
290 static struct timer_slot timer_backup_list[TIMER_BACKUP_SLOTS];
291 static int timer_backup_used = 0;
292
safe_evtimer_add(struct event * ev,struct timeval * tv)293 void safe_evtimer_add(struct event *ev, struct timeval *tv)
294 {
295 int res;
296 struct timer_slot *ts;
297
298 res = evtimer_add(ev, tv);
299 if (res >= 0)
300 return;
301
302 if (timer_backup_used >= TIMER_BACKUP_SLOTS)
303 fatal("TIMER_BACKUP_SLOTS full");
304
305 ts = &timer_backup_list[timer_backup_used++];
306 ts->ev = ev;
307 ts->tv = *tv;
308 }
309
rescue_timers(void)310 void rescue_timers(void)
311 {
312 struct timer_slot *ts;
313 while (timer_backup_used) {
314 ts = &timer_backup_list[timer_backup_used - 1];
315 if (evtimer_add(ts->ev, &ts->tv) < 0)
316 break;
317 timer_backup_used--;
318 }
319 }
320
321
322 /*
323 * PgAddr operations
324 */
325
pga_port(const PgAddr * a)326 int pga_port(const PgAddr *a)
327 {
328 if (a->sa.sa_family == AF_INET6) {
329 return ntohs(a->sin6.sin6_port);
330 } else {
331 return ntohs(a->sin.sin_port);
332 }
333 }
334
335 /* set family and port */
pga_set(PgAddr * a,int af,int port)336 void pga_set(PgAddr *a, int af, int port)
337 {
338 memset(a, 0, sizeof(*a));
339 if (af == AF_INET6) {
340 a->sin6.sin6_family = af;
341 a->sin6.sin6_port = htons(port);
342 } else {
343 a->sin.sin_family = af;
344 a->sin.sin_port = htons(port);
345 }
346 }
347
348 /* copy sockaddr_in/in6 to PgAddr */
pga_copy(PgAddr * a,const struct sockaddr * sa)349 void pga_copy(PgAddr *a, const struct sockaddr *sa)
350 {
351 switch (sa->sa_family) {
352 case AF_INET:
353 memcpy(&a->sin, sa, sizeof(a->sin));
354 break;
355 case AF_INET6:
356 memcpy(&a->sin6, sa, sizeof(a->sin6));
357 break;
358 case AF_UNIX:
359 log_error("pga_copy: AF_UNIX copy not supported");
360 }
361 }
362
pga_cmp_addr(const PgAddr * a,const PgAddr * b)363 int pga_cmp_addr(const PgAddr *a, const PgAddr *b)
364 {
365 if (pga_family(a) != pga_family(b))
366 return pga_family(a) - pga_family(b);
367
368 switch (pga_family(a)) {
369 case AF_INET:
370 return memcmp(&a->sin.sin_addr, &b->sin.sin_addr, sizeof(a->sin.sin_addr));
371 break;
372 case AF_INET6:
373 return memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, sizeof(a->sin6.sin6_addr));
374 break;
375 default:
376 log_error("pga_cmp_addr: unsupported family");
377 return 0;
378 }
379 }
380
381 /* convert pgaddr to string */
pga_ntop(const PgAddr * a,char * dst,int dstlen)382 const char *pga_ntop(const PgAddr *a, char *dst, int dstlen)
383 {
384 const char *res = NULL;
385 char buf[PGADDR_BUF];
386
387 memset(buf, 0, sizeof(buf));
388
389 switch (pga_family(a)) {
390 case AF_UNIX:
391 res = "unix";
392 break;
393 case AF_INET:
394 res = inet_ntop(AF_INET, &a->sin.sin_addr, buf, sizeof(buf));
395 break;
396 case AF_INET6:
397 res = inet_ntop(AF_INET6, &a->sin6.sin6_addr, buf, sizeof(buf));
398 break;
399 default:
400 res = "(bad-af)";
401 }
402 if (res == NULL)
403 res = "(err-ntop)";
404
405 strlcpy(dst, res, dstlen);
406 return dst;
407 }
408
409 /* parse address from string */
pga_pton(PgAddr * a,const char * s,int port)410 bool pga_pton(PgAddr *a, const char *s, int port)
411 {
412 int res = 1;
413 if (strcmp(s, "unix") == 0) {
414 pga_set(a, AF_UNIX, port);
415 } else if (strcmp(s, "*") == 0) {
416 pga_set(a, AF_INET, port);
417 a->sin.sin_addr.s_addr = htonl(INADDR_ANY);
418 } else if (strchr(s, ':')) {
419 pga_set(a, AF_INET6, port);
420 res = inet_pton(AF_INET6, s, &a->sin6.sin6_addr);
421 } else {
422 pga_set(a, AF_INET, port);
423 res = inet_pton(AF_INET, s, &a->sin.sin_addr);
424 }
425 if (res == 0)
426 errno = EINVAL;
427 return res > 0;
428 }
429
pga_str(const PgAddr * a,char * dst,int dstlen)430 const char *pga_str(const PgAddr *a, char *dst, int dstlen)
431 {
432 char buf[PGADDR_BUF];
433 pga_ntop(a, buf, sizeof(buf));
434 if (pga_family(a) == AF_INET6) {
435 snprintf(dst, dstlen, "[%s]:%d", buf, pga_port(a));
436 } else if (pga_family(a) == AF_UNIX && a->scred.pid) {
437 snprintf(dst, dstlen, "%s:%d$%lu", buf, pga_port(a), (unsigned long)a->scred.pid);
438 } else {
439 snprintf(dst, dstlen, "%s:%d", buf, pga_port(a));
440 }
441 return dst;
442 }
443
cached_hostname(void)444 static const char *cached_hostname(void)
445 {
446 static char cache[256];
447 int err;
448
449 if (cache[0] == 0) {
450 err = gethostname(cache, sizeof(cache));
451 if (err != 0)
452 strlcpy(cache, "somehost", sizeof(cache));
453 }
454 return cache;
455 }
456
pga_details(const PgAddr * a,char * dst,int dstlen)457 const char *pga_details(const PgAddr *a, char *dst, int dstlen)
458 {
459 char buf[PGADDR_BUF];
460 pga_ntop(a, buf, sizeof(buf));
461 if (pga_family(a) == AF_INET6) {
462 snprintf(dst, dstlen, "[%s]:%d", buf, pga_port(a));
463 } else if (pga_family(a) == AF_UNIX && a->scred.pid) {
464 snprintf(dst, dstlen, "%s(%lu@%s):%d", buf, (unsigned long)a->scred.pid, cached_hostname(), pga_port(a));
465 } else {
466 snprintf(dst, dstlen, "%s:%d", buf, pga_port(a));
467 }
468 return dst;
469 }
470