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" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
947 * - \c "void **priv_data" — 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" — nazwa serwera do rozwiązania
950 *
951 * Parametry funkcji zwalniającej zasoby wyglądają następująco:
952 * - \c "void **priv_data" — 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" — 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