1 /*
2  *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
3  *                          Robert J. Woźny <speedy@ziew.org>
4  *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
5  *                          Tomasz Chiliński <chilek@chilan.com>
6  *                          Adam Wysocki <gophi@ekg.chmurka.net>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU Lesser General Public License Version
10  *  2.1 as published by the Free Software Foundation.
11  *
12  *  This program 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 Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
20  *  USA.
21  */
22 
23 /**
24  * \file resolver.c
25  *
26  * \brief Funkcje rozwiązywania nazw
27  */
28 
29 #include "internal.h"
30 
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include "strman.h"
36 #include "network.h"
37 #include "resolver.h"
38 #include "session.h"
39 
40 #ifdef GG_CONFIG_HAVE_FORK
41 #include <sys/wait.h>
42 #include <signal.h>
43 #endif
44 
45 /** Sposób rozwiązywania nazw serwerów */
46 static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT;
47 
48 /** Funkcja rozpoczynająca rozwiązywanie nazwy */
49 static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname);
50 
51 /** Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
52 static void (*gg_global_resolver_cleanup)(void **private_data, int force);
53 
54 #ifdef GG_CONFIG_HAVE_PTHREAD
55 
56 #include <pthread.h>
57 
58 /**
59  * \internal Funkcja pomocnicza zwalniająca zasoby po rozwiązywaniu nazwy
60  * w wątku.
61  *
62  * \note Funkcja nie powinna być statyczna, ponieważ zostanie potraktowana
63  * jako inline i kompilator może "zoptymalizować" jej wywołanie w funkcji
64  * pthread_cleanup_pop().
65  *
66  * \param data Wskaźnik na wskaźnik bufora zaalokowanego w wątku
67  */
gg_resolver_cleaner(void * data)68 void gg_resolver_cleaner(void *data)
69 {
70 	void **buf_ptr = (void **) data;
71 
72 	if (buf_ptr != NULL) {
73 		free(*buf_ptr);
74 		*buf_ptr = NULL;
75 	}
76 }
77 
78 #endif /* GG_CONFIG_HAVE_PTHREAD */
79 
80 /**
81  * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
82  *
83  * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
84  * nie, to zwykłej \c gethostbyname. Wynikiem jest tablica adresów zakończona
85  * wartością INADDR_NONE, którą należy zwolnić po użyciu.
86  *
87  * \param hostname Nazwa serwera
88  * \param result Wskaźnik na wskaźnik z tablicą adresów zakończoną INADDR_NONE
89  * \param count Wskaźnik na zmienną, do ktorej zapisze się liczbę wyników
90  * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci
91  *
92  * \return 0 jeśli się powiodło, -1 w przypadku błędu
93  */
gg_gethostbyname_real(const char * hostname,struct in_addr ** result,unsigned int * count,int pthread)94 int gg_gethostbyname_real(const char *hostname, struct in_addr **result, unsigned int *count, int pthread)
95 {
96 #ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R
97 	char *buf = NULL;
98 	char *new_buf = NULL;
99 	struct hostent he;
100 	struct hostent *he_ptr = NULL;
101 	size_t buf_len;
102 	int res;
103 	int h_errnop;
104 	int ret = 0;
105 #ifdef GG_CONFIG_HAVE_PTHREAD
106 	int old_state;
107 #endif
108 
109 	if (result == NULL) {
110 		errno = EINVAL;
111 		return -1;
112 	}
113 
114 #ifdef GG_CONFIG_HAVE_PTHREAD
115 	pthread_cleanup_push(gg_resolver_cleaner, &buf);
116 
117 	if (pthread)
118 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
119 #endif
120 
121 	buf_len = 1024;
122 	res = -1;
123 	buf = malloc(buf_len);
124 
125 #ifdef GG_CONFIG_HAVE_PTHREAD
126 	if (pthread)
127 		pthread_setcancelstate(old_state, NULL);
128 #endif
129 
130 	if (buf != NULL) {
131 		while (1) {
132 #ifndef sun
133 			ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop);
134 			if (ret != ERANGE)
135 				break;
136 #else
137 			he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop);
138 			if (he_ptr != NULL || errno != ERANGE)
139 				break;
140 #endif
141 
142 			buf_len *= 2;
143 
144 #ifdef GG_CONFIG_HAVE_PTHREAD
145 			if (pthread)
146 				pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
147 #endif
148 
149 			new_buf = realloc(buf, buf_len);
150 
151 			if (new_buf != NULL)
152 				buf = new_buf;
153 
154 #ifdef GG_CONFIG_HAVE_PTHREAD
155 			if (pthread)
156 				pthread_setcancelstate(old_state, NULL);
157 #endif
158 
159 			if (new_buf == NULL) {
160 				ret = ENOMEM;
161 				break;
162 			}
163 		}
164 
165 		if (ret == 0 && he_ptr != NULL && he_ptr->h_addr_list[0] != NULL) {
166 			int i;
167 
168 			/* Policz liczbę adresów */
169 
170 			for (i = 0; he_ptr->h_addr_list[i] != NULL; i++);
171 
172 			/* Zaalokuj */
173 
174 #ifdef GG_CONFIG_HAVE_PTHREAD
175 			if (pthread)
176 				pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
177 #endif
178 
179 			*result = malloc((i + 1) * sizeof(struct in_addr));
180 
181 #ifdef GG_CONFIG_HAVE_PTHREAD
182 			if (pthread)
183 				pthread_setcancelstate(old_state, NULL);
184 #endif
185 
186 			if (*result != NULL) {
187 				/* Kopiuj */
188 
189 				for (i = 0; he_ptr->h_addr_list[i] != NULL; i++)
190 					memcpy(&((*result)[i]), he_ptr->h_addr_list[i], sizeof(struct in_addr));
191 
192 				(*result)[i].s_addr = INADDR_NONE;
193 
194 				*count = i;
195 
196 				res = 0;
197 			} else
198 				res = -1;
199 		}
200 
201 #ifdef GG_CONFIG_HAVE_PTHREAD
202 		if (pthread)
203 			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
204 #endif
205 
206 		free(buf);
207 		buf = NULL;
208 
209 #ifdef GG_CONFIG_HAVE_PTHREAD
210 		if (pthread)
211 			pthread_setcancelstate(old_state, NULL);
212 #endif
213 	}
214 
215 #ifdef GG_CONFIG_HAVE_PTHREAD
216 	pthread_cleanup_pop(0);
217 #endif
218 
219 	return res;
220 #else /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
221 	struct hostent *he;
222 	int i;
223 #ifdef GG_CONFIG_HAVE_PTHREAD
224 	int old_state;
225 #endif
226 
227 	if (result == NULL || count == NULL) {
228 		errno = EINVAL;
229 		return -1;
230 	}
231 
232 	he = gethostbyname(hostname);
233 
234 	if (he == NULL || he->h_addr_list[0] == NULL)
235 		return -1;
236 
237 	/* Policz liczbę adresów */
238 
239 	for (i = 0; he->h_addr_list[i] != NULL; i++);
240 
241 	/* Zaalokuj */
242 
243 #ifdef GG_CONFIG_HAVE_PTHREAD
244 	if (pthread)
245 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
246 #endif
247 
248 	*result = malloc((i + 1) * sizeof(struct in_addr));
249 
250 #ifdef GG_CONFIG_HAVE_PTHREAD
251 	if (pthread)
252 		pthread_setcancelstate(old_state, NULL);
253 #endif
254 
255 	if (*result == NULL)
256 		return -1;
257 
258 	/* Kopiuj */
259 
260 	for (i = 0; he->h_addr_list[i] != NULL; i++)
261 		memcpy(&((*result)[i]), he->h_addr_list[i], sizeof(struct in_addr));
262 
263 	(*result)[i].s_addr = INADDR_NONE;
264 
265 	*count = i;
266 
267 	return 0;
268 #endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
269 }
270 
271 /**
272  * \internal Rozwiązuje nazwę i zapisuje wynik do podanego gniazda.
273  *
274  * \note Użycie logowania w tej funkcji może mieć negatywny wpływ na
275  * aplikacje jednowątkowe korzystające.
276  *
277  * \param fd Deskryptor gniazda
278  * \param hostname Nazwa serwera
279  * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci
280  *
281  * \return 0 jeśli się powiodło, -1 w przypadku błędu
282  */
gg_resolver_run(int fd,const char * hostname,int pthread)283 static int gg_resolver_run(int fd, const char *hostname, int pthread)
284 {
285 	struct in_addr addr_ip[2], *addr_list = NULL;
286 	unsigned int addr_count;
287 	int res;
288 #ifdef GG_CONFIG_HAVE_PTHREAD
289 	int old_state;
290 #endif
291 
292 #ifdef GG_CONFIG_HAVE_PTHREAD
293 	pthread_cleanup_push(gg_resolver_cleaner, &addr_list);
294 #endif
295 
296 	res = 0;
297 
298 	if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) {
299 		if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, pthread) == -1) {
300 #ifdef GG_CONFIG_HAVE_PTHREAD
301 			if (pthread)
302 				pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
303 #endif
304 
305 			free(addr_list);
306 			addr_list = NULL;
307 
308 #ifdef GG_CONFIG_HAVE_PTHREAD
309 			if (pthread)
310 				pthread_setcancelstate(old_state, NULL);
311 #endif
312 
313 			addr_count = 0;
314 			/* addr_ip[0] już zawiera INADDR_NONE */
315 		}
316 	} else {
317 		addr_ip[1].s_addr = INADDR_NONE;
318 		addr_count = 1;
319 	}
320 
321 	if (send(fd, addr_list != NULL ? addr_list : addr_ip,
322 		(addr_count + 1) * sizeof(struct in_addr), 0) !=
323 		(int)((addr_count + 1) * sizeof(struct in_addr)))
324 	{
325 		res = -1;
326 	}
327 
328 #ifdef GG_CONFIG_HAVE_PTHREAD
329 	if (pthread)
330 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
331 #endif
332 
333 	free(addr_list);
334 	addr_list = NULL;
335 
336 #ifdef GG_CONFIG_HAVE_PTHREAD
337 	if (pthread)
338 		pthread_setcancelstate(old_state, NULL);
339 
340 	pthread_cleanup_pop(0);
341 #endif
342 
343 	return res;
344 }
345 
346 /**
347  * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
348  *
349  * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
350  * nie, to zwykłej \c gethostbyname. Funkcja służy do zachowania zgodności
351  * ABI i służy do pobierania tylko pierwszego adresu -- pozostałe mogą
352  * zostać zignorowane przez aplikację.
353  *
354  * \param hostname Nazwa serwera
355  *
356  * \return Zaalokowana struktura \c in_addr lub NULL w przypadku błędu.
357  */
gg_gethostbyname(const char * hostname)358 struct in_addr *gg_gethostbyname(const char *hostname)
359 {
360 	struct in_addr *result;
361 	unsigned int count;
362 
363 	if (gg_gethostbyname_real(hostname, &result, &count, 0) == -1)
364 		return NULL;
365 
366 	return result;
367 }
368 
369 #ifdef GG_CONFIG_HAVE_FORK
370 
371 /**
372  * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
373  */
374 struct gg_resolver_fork_data {
375 	int pid;		/*< Identyfikator procesu */
376 };
377 
378 /**
379  * \internal Rozwiązuje nazwę serwera w osobnym procesie.
380  *
381  * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania
382  * nazwy serwera. W tym celu tworzona jest para gniazd, nowy proces i dopiero
383  * w nim przeprowadzane jest rozwiązywanie nazwy. Deskryptor gniazda do odczytu
384  * zapisuje się w strukturze sieci i czeka na dane w postaci struktury
385  * \c in_addr. Jeśli nie znaleziono nazwy, zwracana jest \c INADDR_NONE.
386  *
387  * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
388  * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
389  *                  do numeru procesu potomnego rozwiązującego nazwę
390  * \param hostname Nazwa serwera do rozwiązania
391  *
392  * \return 0 jeśli się powiodło, -1 w przypadku błędu
393  */
gg_resolver_fork_start(int * fd,void ** priv_data,const char * hostname)394 static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname)
395 {
396 	struct gg_resolver_fork_data *data = NULL;
397 	int pipes[2], new_errno;
398 
399 	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
400 
401 	if (fd == NULL || priv_data == NULL || hostname == NULL) {
402 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n");
403 		errno = EFAULT;
404 		return -1;
405 	}
406 
407 	data = malloc(sizeof(struct gg_resolver_fork_data));
408 
409 	if (data == NULL) {
410 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n");
411 		return -1;
412 	}
413 
414 	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
415 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable "
416 			"to create pipes (errno=%d, %s)\n",
417 			errno, strerror(errno));
418 		free(data);
419 		return -1;
420 	}
421 
422 	data->pid = fork();
423 
424 	if (data->pid == -1) {
425 		new_errno = errno;
426 		goto cleanup;
427 	}
428 
429 	if (data->pid == 0) {
430 		int status;
431 
432 		close(pipes[0]);
433 
434 		status = (gg_resolver_run(pipes[1], hostname, 0) == -1) ? 1 : 0;
435 
436 #ifdef HAVE__EXIT
437 		_exit(status);
438 #else
439 		exit(status);
440 #endif
441 	}
442 
443 	close(pipes[1]);
444 
445 	gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data);
446 
447 	*fd = pipes[0];
448 	*priv_data = data;
449 
450 	return 0;
451 
452 cleanup:
453 	free(data);
454 	close(pipes[0]);
455 	close(pipes[1]);
456 
457 	errno = new_errno;
458 
459 	return -1;
460 }
461 
462 /**
463  * \internal Usuwanie zasobów po procesie rozwiązywaniu nazwy.
464  *
465  * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
466  * zasobów sesji podczas rozwiązywania nazwy.
467  *
468  * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
469  *                  danych
470  * \param force Flaga usuwania zasobów przed zakończeniem działania
471  */
gg_resolver_fork_cleanup(void ** priv_data,int force)472 static void gg_resolver_fork_cleanup(void **priv_data, int force)
473 {
474 	struct gg_resolver_fork_data *data;
475 
476 	if (priv_data == NULL || *priv_data == NULL)
477 		return;
478 
479 	data = (struct gg_resolver_fork_data*) *priv_data;
480 	*priv_data = NULL;
481 
482 	if (force)
483 		kill(data->pid, SIGKILL);
484 
485 	/* we don't care about child's exit status, just want to clean it up */
486 	(void)waitpid(data->pid, NULL, WNOHANG);
487 
488 	free(data);
489 }
490 
491 #endif /* GG_CONFIG_HAVE_FORK */
492 
493 #ifdef GG_CONFIG_HAVE_PTHREAD
494 
495 /**
496  * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
497  */
498 struct gg_resolver_pthread_params {
499 	pthread_barrier_t *init_barrier; /*< Bariera pilnująca poprawnej inicjalizacji wątku */
500 	char *hostname;		/*< Nazwa serwera */
501 	int wfd;		/*< Deskryptor do zapisu */
502 };
503 
504 /**
505  * \internal Struktura opisująca wątek rozwiązujący nazwę.
506  */
507 struct gg_resolver_pthread_data {
508 	pthread_t thread;	/*< Identyfikator wątku */
509 };
510 
511 /**
512  * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy.
513  *
514  * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
515  * zasobów sesji podczas rozwiązywania nazwy.
516  *
517  * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
518  *                  danych
519  * \param force Flaga usuwania zasobów przed zakończeniem działania
520  */
gg_resolver_pthread_cleanup(void ** priv_data,int force)521 static void gg_resolver_pthread_cleanup(void **priv_data, int force)
522 {
523 	struct gg_resolver_pthread_data *data;
524 
525 	if (priv_data == NULL || *priv_data == NULL)
526 		return;
527 
528 	data = (struct gg_resolver_pthread_data *) *priv_data;
529 	*priv_data = NULL;
530 
531 #ifdef _WIN32
532 	/* Mingw's implementation of pthreads seems to not like pthread_cancel.
533 	 * Let's detach the thread and let it complete in background.
534 	 */
535 	if (force)
536 		pthread_detach(data->thread);
537 	else
538 		pthread_join(data->thread, NULL);
539 #else
540 	if (force)
541 		pthread_cancel(data->thread);
542 
543 	pthread_join(data->thread, NULL);
544 #endif
545 
546 	free(data);
547 }
548 
gg_resolver_pthread_params_cleanup(void * params_raw)549 static void gg_resolver_pthread_params_cleanup(void *params_raw) {
550 	struct gg_resolver_pthread_params *params = params_raw;
551 
552 	close(params->wfd);
553 	free(params->hostname);
554 	free(params);
555 }
556 
557 /**
558  * \internal Wątek rozwiązujący nazwę.
559  *
560  * \param arg Wskaźnik na strukturę \c gg_resolver_pthread_params
561  */
gg_resolver_pthread_thread(void * arg)562 static void *gg_resolver_pthread_thread(void *arg)
563 {
564 	struct gg_resolver_pthread_params *params = arg;
565 	int res;
566 
567 	pthread_cleanup_push(gg_resolver_pthread_params_cleanup, params);
568 
569 	/* Powiadom wątek główny, że już odebraliśmy parametry. */
570 	pthread_barrier_wait(params->init_barrier);
571 
572 	res = gg_resolver_run(params->wfd, params->hostname, 1);
573 
574 	pthread_cleanup_pop(1);
575 
576 	pthread_exit((void*)(intptr_t)res);
577 	return NULL;	/* żeby kompilator nie marudził */
578 }
579 
580 /**
581  * \internal Rozwiązuje nazwę serwera w osobnym wątku.
582  *
583  * Funkcja działa analogicznie do \c gg_resolver_fork_start(), z tą różnicą,
584  * że działa na wątkach, nie procesach. Jest dostępna wyłącznie gdy podczas
585  * kompilacji włączono odpowiednią opcję.
586  *
587  * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
588  * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
589  *                  do prywatnych danych wątku rozwiązującego nazwę
590  * \param hostname Nazwa serwera do rozwiązania
591  *
592  * \return 0 jeśli się powiodło, -1 w przypadku błędu
593  */
gg_resolver_pthread_start(int * fd,void ** priv_data,const char * hostname)594 static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname)
595 {
596 	struct gg_resolver_pthread_data *data = NULL;
597 	struct gg_resolver_pthread_params *params = NULL;
598 	int pipes[2], pipe_ready = 0, new_errno;
599 	pthread_barrier_t init_barrier;
600 
601 	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
602 
603 	if (fd == NULL || priv_data == NULL || hostname == NULL) {
604 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n");
605 		errno = EFAULT;
606 		return -1;
607 	}
608 
609 	data = malloc(sizeof(struct gg_resolver_pthread_data));
610 	if (data == NULL) {
611 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() "
612 			"out of memory for resolver data\n");
613 		goto cleanup;
614 	}
615 
616 	params = malloc(sizeof(struct gg_resolver_pthread_params));
617 	if (params == NULL) {
618 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() "
619 			"out of memory for resolver parameters\n");
620 		goto cleanup;
621 	}
622 
623 	params->hostname = strdup(hostname);
624 	if (params->hostname == NULL) {
625 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() "
626 			"out of memory for hostname\n");
627 		goto cleanup;
628 	}
629 
630 	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
631 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable "
632 			"to create pipes (errno=%d, %s)\n",
633 			errno, strerror(errno));
634 		goto cleanup;
635 	}
636 	params->wfd = pipes[1];
637 	pipe_ready = 1;
638 
639 	if (pthread_barrier_init(&init_barrier, NULL, 2) != 0) {
640 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() "
641 			"can't create barrier\n");
642 		goto cleanup;
643 	}
644 	params->init_barrier = &init_barrier;
645 
646 	if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, params)) {
647 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() "
648 			"unable to create thread\n");
649 		pthread_barrier_destroy(&init_barrier);
650 		goto cleanup;
651 	}
652 
653 	gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data);
654 
655 	/* Poczekaj, aż wątek rozwiązywania nazw potwierdzi odebranie
656 	 * parametrów, aby mieć pewność, że go nie zabijemy zanim je zwolni.
657 	 */
658 	pthread_barrier_wait(&init_barrier);
659 	pthread_barrier_destroy(&init_barrier);
660 
661 	*fd = pipes[0];
662 	*priv_data = data;
663 
664 	return 0;
665 
666 cleanup:
667 	new_errno = errno;
668 
669 	free(data);
670 
671 	if (params != NULL)
672 		free(params->hostname);
673 	free(params);
674 
675 	if (pipe_ready) {
676 		close(pipes[0]);
677 		close(pipes[1]);
678 	}
679 
680 	errno = new_errno;
681 
682 	return -1;
683 }
684 
685 #endif /* GG_CONFIG_HAVE_PTHREAD */
686 
687 #ifdef _WIN32
688 
689 /**
690  * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
691  */
692 struct gg_resolver_win32_data {
693 	HANDLE thread;		/*< Uchwyt wątku */
694 	CRITICAL_SECTION mutex;	/*< Semafor wątku */
695 	char *hostname;		/*< Nazwa serwera */
696 	int wfd;		/*< Deskryptor do zapisu */
697 	int orphan;		/*< Wątek powinien sam po sobie posprzątać */
698 	int finished;		/*< Wątek już skończył pracę */
699 };
700 
701 /**
702  * \internal Wątek rozwiązujący nazwę.
703  *
704  * \param arg Wskaźnik na strukturę \c gg_resolver_win32_data
705  */
gg_resolver_win32_thread(void * arg)706 static DWORD WINAPI gg_resolver_win32_thread(void *arg)
707 {
708 	struct gg_resolver_win32_data *data = arg;
709 	int result, is_orphan;
710 
711 	result = gg_resolver_run(data->wfd, data->hostname, 0);
712 
713 	EnterCriticalSection(&data->mutex);
714 	is_orphan = data->orphan;
715 	data->finished = 1;
716 	LeaveCriticalSection(&data->mutex);
717 
718 	if (is_orphan) {
719 		CloseHandle(data->thread);
720 		DeleteCriticalSection(&data->mutex);
721 		close(data->wfd);
722 		free(data->hostname);
723 		free(data);
724 	}
725 
726 	ExitThread(result);
727 	return 0; /* żeby kompilator nie marudził */
728 }
729 
730 /**
731  * \internal Rozwiązuje nazwę serwera w osobnym wątku.
732  *
733  * Funkcja działa analogicznie do \c gg_resolver_pthread_start(), z tą różnicą,
734  * że działa na wątkach Win32. Jest dostępna wyłącznie przy kompilacji dla
735  * systemu Windows.
736  *
737  * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
738  * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
739  *                  do prywatnych danych wątku rozwiązującego nazwę
740  * \param hostname Nazwa serwera do rozwiązania
741  *
742  * \return 0 jeśli się powiodło, -1 w przypadku błędu
743  */
gg_resolver_win32_start(int * fd,void ** priv_data,const char * hostname)744 static int gg_resolver_win32_start(int *fd, void **priv_data, const char *hostname)
745 {
746 	struct gg_resolver_win32_data *data = NULL;
747 	int pipes[2], new_errno;
748 	CRITICAL_SECTION *mutex = NULL;
749 
750 	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_win32_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
751 
752 	if (fd == NULL || priv_data == NULL || hostname == NULL) {
753 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() invalid arguments\n");
754 		errno = EFAULT;
755 		return -1;
756 	}
757 
758 	data = malloc(sizeof(struct gg_resolver_win32_data));
759 
760 	if (data == NULL) {
761 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() out of memory for resolver data\n");
762 		return -1;
763 	}
764 
765 	data->orphan = 0;
766 	data->finished = 0;
767 
768 	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
769 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() unable to "
770 			"create pipes (errno=%d, %s)\n",
771 			errno, strerror(errno));
772 		free(data);
773 		return -1;
774 	}
775 
776 	data->hostname = strdup(hostname);
777 
778 	if (data->hostname == NULL) {
779 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() out of memory\n");
780 		new_errno = errno;
781 		goto cleanup;
782 	}
783 
784 	data->wfd = pipes[1];
785 
786 	mutex = &data->mutex;
787 	InitializeCriticalSection(mutex);
788 
789 	data->thread = CreateThread(NULL, 0, gg_resolver_win32_thread, data, 0, NULL);
790 	if (!data->thread) {
791 		gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() unable to create thread\n");
792 		new_errno = errno;
793 		goto cleanup;
794 	}
795 
796 	gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() %p\n", data);
797 
798 	*fd = pipes[0];
799 	*priv_data = data;
800 
801 	return 0;
802 
803 cleanup:
804 	if (data) {
805 		free(data->hostname);
806 		free(data);
807 	}
808 
809 	close(pipes[0]);
810 	close(pipes[1]);
811 
812 	if (mutex)
813 		DeleteCriticalSection(mutex);
814 
815 	errno = new_errno;
816 
817 	return -1;
818 }
819 
820 /**
821  * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy.
822  *
823  * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
824  * zasobów sesji podczas rozwiązywania nazwy.
825  *
826  * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
827  *                  danych
828  * \param force Flaga usuwania zasobów przed zakończeniem działania
829  */
gg_resolver_win32_cleanup(void ** priv_data,int force)830 static void gg_resolver_win32_cleanup(void **priv_data, int force)
831 {
832 	struct gg_resolver_win32_data *data;
833 
834 	if (priv_data == NULL || *priv_data == NULL)
835 		return;
836 
837 	data = (struct gg_resolver_win32_data *) *priv_data;
838 	*priv_data = NULL;
839 
840 	if (WaitForSingleObject(data->thread, 0) == WAIT_TIMEOUT) {
841 		int finished;
842 		/* We cannot call TerminateThread here - it doesn't
843 		 * release critical section locks (see MSDN docs).
844 		 * if (force) TerminateThread(data->thread, 0);
845 		 */
846 		EnterCriticalSection(&data->mutex);
847 		finished = data->finished;
848 		if (!finished)
849 			data->orphan = 1;
850 		LeaveCriticalSection(&data->mutex);
851 		if (!finished)
852 			return;
853 	}
854 
855 	CloseHandle(data->thread);
856 	DeleteCriticalSection(&data->mutex);
857 	close(data->wfd);
858 	free(data->hostname);
859 	free(data);
860 }
861 
862 #endif /* _WIN32 */
863 
864 /**
865  * Ustawia sposób rozwiązywania nazw w sesji.
866  *
867  * \param gs Struktura sesji
868  * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
869  *
870  * \return 0 jeśli się powiodło, -1 w przypadku błędu
871  */
gg_session_set_resolver(struct gg_session * gs,gg_resolver_t type)872 int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)
873 {
874 	GG_SESSION_CHECK(gs, -1);
875 
876 	if (type == GG_RESOLVER_DEFAULT) {
877 		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
878 			gs->resolver_type = gg_global_resolver_type;
879 			gs->resolver_start = gg_global_resolver_start;
880 			gs->resolver_cleanup = gg_global_resolver_cleanup;
881 			return 0;
882 		}
883 
884 #ifdef _WIN32
885 		type = GG_RESOLVER_WIN32;
886 #elif defined(GG_CONFIG_HAVE_PTHREAD)
887 		type = GG_RESOLVER_PTHREAD;
888 #elif defined(GG_CONFIG_HAVE_FORK)
889 		type = GG_RESOLVER_FORK;
890 #endif
891 	}
892 
893 	switch (type) {
894 #ifdef GG_CONFIG_HAVE_FORK
895 		case GG_RESOLVER_FORK:
896 			gs->resolver_type = type;
897 			gs->resolver_start = gg_resolver_fork_start;
898 			gs->resolver_cleanup = gg_resolver_fork_cleanup;
899 			return 0;
900 #endif
901 
902 #ifdef GG_CONFIG_HAVE_PTHREAD
903 		case GG_RESOLVER_PTHREAD:
904 			gs->resolver_type = type;
905 			gs->resolver_start = gg_resolver_pthread_start;
906 			gs->resolver_cleanup = gg_resolver_pthread_cleanup;
907 			return 0;
908 #endif
909 
910 #ifdef _WIN32
911 		case GG_RESOLVER_WIN32:
912 			gs->resolver_type = type;
913 			gs->resolver_start = gg_resolver_win32_start;
914 			gs->resolver_cleanup = gg_resolver_win32_cleanup;
915 			return 0;
916 #endif
917 
918 		default:
919 			errno = EINVAL;
920 			return -1;
921 	}
922 }
923 
924 /**
925  * Zwraca sposób rozwiązywania nazw w sesji.
926  *
927  * \param gs Struktura sesji
928  *
929  * \return Sposób rozwiązywania nazw
930  */
gg_session_get_resolver(struct gg_session * gs)931 gg_resolver_t gg_session_get_resolver(struct gg_session *gs)
932 {
933 	GG_SESSION_CHECK(gs, (gg_resolver_t) -1);
934 
935 	return gs->resolver_type;
936 }
937 
938 /**
939  * Ustawia własny sposób rozwiązywania nazw w sesji.
940  *
941  * \param gs Struktura sesji
942  * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
943  * \param resolver_cleanup Funkcja zwalniająca zasoby
944  *
945  * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco:
946  *  - \c "int *fd" &mdash; wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
947  *  - \c "void **priv_data" &mdash; wskaźnik na zmienną, gdzie można umieścić
948  *    wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy
949  *  - \c "const char *name" &mdash; nazwa serwera do rozwiązania
950  *
951  * Parametry funkcji zwalniającej zasoby wyglądają następująco:
952  *  - \c "void **priv_data" &mdash; wskaźnik na zmienną przechowującą wskaźnik
953  *    do prywatnych danych, należy go ustawić na \c NULL po zakończeniu
954  *  - \c "int force" &mdash; flaga mówiąca o tym, że zasoby są zwalniane przed
955  *    zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji.
956  *
957  * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub
958  * inny deskryptor pozwalający na co najmniej odbiór danych i przekazać go
959  * w parametrze \c fd. Na platformie Windows możliwe jest przekazanie jedynie
960  * deskryptora gniazda. Po zakończeniu rozwiązywania nazwy powinien wysłać
961  * otrzymany adres IP w postaci sieciowej (big-endian) do deskryptora. Jeśli
962  * rozwiązywanie nazwy się nie powiedzie, należy wysłać \c INADDR_NONE.
963  * Następnie zostanie wywołana funkcja zwalniająca zasoby z parametrem
964  * \c force równym \c 0. Gdyby sesja została zakończona przed rozwiązaniem
965  * nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja zwalniająca zasoby
966  * zostanie wywołana z parametrem \c force równym \c 1.
967  *
968  * \return 0 jeśli się powiodło, -1 w przypadku błędu
969  */
gg_session_set_custom_resolver(struct gg_session * gs,int (* resolver_start)(int *,void **,const char *),void (* resolver_cleanup)(void **,int))970 int gg_session_set_custom_resolver(struct gg_session *gs,
971 	int (*resolver_start)(int*, void**, const char*),
972 	void (*resolver_cleanup)(void**, int))
973 {
974 	GG_SESSION_CHECK(gs, -1);
975 
976 	if (resolver_start == NULL || resolver_cleanup == NULL) {
977 		errno = EINVAL;
978 		return -1;
979 	}
980 
981 	gs->resolver_type = GG_RESOLVER_CUSTOM;
982 	gs->resolver_start = resolver_start;
983 	gs->resolver_cleanup = resolver_cleanup;
984 
985 	return 0;
986 }
987 
988 /**
989  * Ustawia sposób rozwiązywania nazw połączenia HTTP.
990  *
991  * \param gh Struktura połączenia
992  * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
993  *
994  * \return 0 jeśli się powiodło, -1 w przypadku błędu
995  */
gg_http_set_resolver(struct gg_http * gh,gg_resolver_t type)996 int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type)
997 {
998 	if (gh == NULL) {
999 		errno = EINVAL;
1000 		return -1;
1001 	}
1002 
1003 	if (type == GG_RESOLVER_DEFAULT) {
1004 		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
1005 			gh->resolver_type = gg_global_resolver_type;
1006 			gh->resolver_start = gg_global_resolver_start;
1007 			gh->resolver_cleanup = gg_global_resolver_cleanup;
1008 			return 0;
1009 		}
1010 
1011 #ifdef _WIN32
1012 		type = GG_RESOLVER_WIN32;
1013 #elif defined(GG_CONFIG_HAVE_PTHREAD)
1014 		type = GG_RESOLVER_PTHREAD;
1015 #elif defined(GG_CONFIG_HAVE_FORK)
1016 		type = GG_RESOLVER_FORK;
1017 #endif
1018 	}
1019 
1020 	switch (type) {
1021 #ifdef GG_CONFIG_HAVE_FORK
1022 		case GG_RESOLVER_FORK:
1023 			gh->resolver_type = type;
1024 			gh->resolver_start = gg_resolver_fork_start;
1025 			gh->resolver_cleanup = gg_resolver_fork_cleanup;
1026 			return 0;
1027 #endif
1028 
1029 #ifdef GG_CONFIG_HAVE_PTHREAD
1030 		case GG_RESOLVER_PTHREAD:
1031 			gh->resolver_type = type;
1032 			gh->resolver_start = gg_resolver_pthread_start;
1033 			gh->resolver_cleanup = gg_resolver_pthread_cleanup;
1034 			return 0;
1035 #endif
1036 
1037 #ifdef _WIN32
1038 		case GG_RESOLVER_WIN32:
1039 			gh->resolver_type = type;
1040 			gh->resolver_start = gg_resolver_win32_start;
1041 			gh->resolver_cleanup = gg_resolver_win32_cleanup;
1042 			return 0;
1043 #endif
1044 
1045 		default:
1046 			errno = EINVAL;
1047 			return -1;
1048 	}
1049 }
1050 
1051 /**
1052  * Zwraca sposób rozwiązywania nazw połączenia HTTP.
1053  *
1054  * \param gh Struktura połączenia
1055  *
1056  * \return Sposób rozwiązywania nazw
1057  */
gg_http_get_resolver(struct gg_http * gh)1058 gg_resolver_t gg_http_get_resolver(struct gg_http *gh)
1059 {
1060 	if (gh == NULL) {
1061 		errno = EINVAL;
1062 		return GG_RESOLVER_INVALID;
1063 	}
1064 
1065 	return gh->resolver_type;
1066 }
1067 
1068 /**
1069  * Ustawia własny sposób rozwiązywania nazw połączenia HTTP.
1070  *
1071  * \param gh Struktura sesji
1072  * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
1073  * \param resolver_cleanup Funkcja zwalniająca zasoby
1074  *
1075  * \return 0 jeśli się powiodło, -1 w przypadku błędu
1076  */
gg_http_set_custom_resolver(struct gg_http * gh,int (* resolver_start)(int *,void **,const char *),void (* resolver_cleanup)(void **,int))1077 int gg_http_set_custom_resolver(struct gg_http *gh,
1078 	int (*resolver_start)(int*, void**, const char*),
1079 	void (*resolver_cleanup)(void**, int))
1080 {
1081 	if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
1082 		errno = EINVAL;
1083 		return -1;
1084 	}
1085 
1086 	gh->resolver_type = GG_RESOLVER_CUSTOM;
1087 	gh->resolver_start = resolver_start;
1088 	gh->resolver_cleanup = resolver_cleanup;
1089 
1090 	return 0;
1091 }
1092 
1093 /**
1094  * Ustawia sposób rozwiązywania nazw globalnie dla biblioteki.
1095  *
1096  * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
1097  *
1098  * \return 0 jeśli się powiodło, -1 w przypadku błędu
1099  */
gg_global_set_resolver(gg_resolver_t type)1100 int gg_global_set_resolver(gg_resolver_t type)
1101 {
1102 	switch (type) {
1103 		case GG_RESOLVER_DEFAULT:
1104 			gg_global_resolver_type = type;
1105 			gg_global_resolver_start = NULL;
1106 			gg_global_resolver_cleanup = NULL;
1107 			return 0;
1108 
1109 #ifdef GG_CONFIG_HAVE_FORK
1110 		case GG_RESOLVER_FORK:
1111 			gg_global_resolver_type = type;
1112 			gg_global_resolver_start = gg_resolver_fork_start;
1113 			gg_global_resolver_cleanup = gg_resolver_fork_cleanup;
1114 			return 0;
1115 #endif
1116 
1117 #ifdef GG_CONFIG_HAVE_PTHREAD
1118 		case GG_RESOLVER_PTHREAD:
1119 			gg_global_resolver_type = type;
1120 			gg_global_resolver_start = gg_resolver_pthread_start;
1121 			gg_global_resolver_cleanup = gg_resolver_pthread_cleanup;
1122 			return 0;
1123 #endif
1124 
1125 #ifdef _WIN32
1126 		case GG_RESOLVER_WIN32:
1127 			gg_global_resolver_type = type;
1128 			gg_global_resolver_start = gg_resolver_win32_start;
1129 			gg_global_resolver_cleanup = gg_resolver_win32_cleanup;
1130 			return 0;
1131 #endif
1132 
1133 		default:
1134 			errno = EINVAL;
1135 			return -1;
1136 	}
1137 }
1138 
1139 /**
1140  * Zwraca sposób rozwiązywania nazw globalnie dla biblioteki.
1141  *
1142  * \return Sposób rozwiązywania nazw
1143  */
gg_global_get_resolver(void)1144 gg_resolver_t gg_global_get_resolver(void)
1145 {
1146 	return gg_global_resolver_type;
1147 }
1148 
1149 /**
1150  * Ustawia własny sposób rozwiązywania nazw globalnie dla biblioteki.
1151  *
1152  * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
1153  * \param resolver_cleanup Funkcja zwalniająca zasoby
1154  *
1155  * Patrz \ref gg_session_set_custom_resolver.
1156  *
1157  * \return 0 jeśli się powiodło, -1 w przypadku błędu
1158  */
gg_global_set_custom_resolver(int (* resolver_start)(int *,void **,const char *),void (* resolver_cleanup)(void **,int))1159 int gg_global_set_custom_resolver(
1160 	int (*resolver_start)(int*, void**, const char*),
1161 	void (*resolver_cleanup)(void**, int))
1162 {
1163 	if (resolver_start == NULL || resolver_cleanup == NULL) {
1164 		errno = EINVAL;
1165 		return -1;
1166 	}
1167 
1168 	gg_global_resolver_type = GG_RESOLVER_CUSTOM;
1169 	gg_global_resolver_start = resolver_start;
1170 	gg_global_resolver_cleanup = resolver_cleanup;
1171 
1172 	return 0;
1173 }
1174 
1175 /**
1176  * Odczytuje dane z procesu/wątku rozwiązywania nazw.
1177  *
1178  * \param fd Deskryptor
1179  * \param buf Wskaźnik na bufor
1180  * \param len Długość bufora
1181  *
1182  * \return Patrz recv() i read().
1183  */
gg_resolver_recv(int fd,void * buf,size_t len)1184 int gg_resolver_recv(int fd, void *buf, size_t len)
1185 {
1186 #ifndef _WIN32
1187 	return read(fd, buf, len);
1188 #else
1189 	return recv(fd, buf, len, 0);
1190 #endif
1191 }
1192