--- a/libpurple/protocols/gg/lib/resolver.c Wed Sep 28 09:32:19 2016 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1132 +0,0 @@ -/* - * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Woźny <speedy@ziew.org> - * Arkadiusz Miśkiewicz <arekm@pld-linux.org> - * Tomasz Chiliński <chilek@chilan.com> - * Adam Wysocki <gophi@ekg.chmurka.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -/** - * \file resolver.c - * - * \brief Funkcje rozwiązywania nazw - */ - -#include <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "strman.h" -#include "network.h" -#include "config.h" -#include "libgadu.h" -#include "resolver.h" -#include "session.h" - -#ifdef GG_CONFIG_HAVE_FORK -#include <sys/wait.h> -#include <signal.h> -#endif - -/** Sposób rozwiązywania nazw serwerów */ -static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; - -/** Funkcja rozpoczynająca rozwiązywanie nazwy */ -static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname); - -/** Funkcja zwalniająca zasoby po rozwiązaniu nazwy */ -static void (*gg_global_resolver_cleanup)(void **private_data, int force); - -#ifdef GG_CONFIG_HAVE_PTHREAD - -#include <pthread.h> - -/** - * \internal Funkcja pomocnicza zwalniająca zasoby po rozwiązywaniu nazwy - * w wątku. - * - * \note Funkcja nie powinna być statyczna, ponieważ zostanie potraktowana - * jako inline i kompilator może "zoptymalizować" jej wywołanie w funkcji - * pthread_cleanup_pop(). - * - * \param data Wskaźnik na wskaźnik bufora zaalokowanego w wątku - */ -void gg_resolver_cleaner(void *data) -{ - void **buf_ptr = (void **) data; - - if (buf_ptr != NULL) { - free(*buf_ptr); - *buf_ptr = NULL; - } -} - -#endif /* GG_CONFIG_HAVE_PTHREAD */ - -/** - * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. - * - * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli - * nie, to zwykłej \c gethostbyname. Wynikiem jest tablica adresów zakończona - * wartością INADDR_NONE, którą należy zwolnić po użyciu. - * - * \param hostname Nazwa serwera - * \param result Wskaźnik na wskaźnik z tablicą adresów zakończoną INADDR_NONE - * \param count Wskaźnik na zmienną, do ktorej zapisze się liczbę wyników - * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -int gg_gethostbyname_real(const char *hostname, struct in_addr **result, unsigned int *count, int pthread) -{ -#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R - char *buf = NULL; - char *new_buf = NULL; - struct hostent he; - struct hostent *he_ptr = NULL; - size_t buf_len = 1024; - int res = -1; - int h_errnop; - int ret = 0; -#ifdef GG_CONFIG_HAVE_PTHREAD - int old_state; -#endif - - if (result == NULL) { - errno = EINVAL; - return -1; - } - -#ifdef GG_CONFIG_HAVE_PTHREAD - pthread_cleanup_push(gg_resolver_cleaner, &buf); - - if (pthread) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); -#endif - - buf = malloc(buf_len); - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(old_state, NULL); -#endif - - if (buf != NULL) { - while (1) { -#ifndef sun - ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop); - if (ret != ERANGE) - break; -#else - he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop); - if (he_ptr != NULL || errno != ERANGE) - break; -#endif - - buf_len *= 2; - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); -#endif - - new_buf = realloc(buf, buf_len); - - if (new_buf != NULL) - buf = new_buf; - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(old_state, NULL); -#endif - - if (new_buf == NULL) { - ret = ENOMEM; - break; - } - } - - if (ret == 0 && he_ptr != NULL && he_ptr->h_addr_list[0] != NULL) { - int i; - - /* Policz liczbę adresów */ - - for (i = 0; he_ptr->h_addr_list[i] != NULL; i++); - - /* Zaalokuj */ - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); -#endif - - *result = malloc((i + 1) * sizeof(struct in_addr)); - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(old_state, NULL); -#endif - - if (*result != NULL) { - /* Kopiuj */ - - for (i = 0; he_ptr->h_addr_list[i] != NULL; i++) - memcpy(&((*result)[i]), he_ptr->h_addr_list[i], sizeof(struct in_addr)); - - (*result)[i].s_addr = INADDR_NONE; - - *count = i; - - res = 0; - } else - res = -1; - } - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); -#endif - - free(buf); - buf = NULL; - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(old_state, NULL); -#endif - } - -#ifdef GG_CONFIG_HAVE_PTHREAD - pthread_cleanup_pop(0); -#endif - - return res; -#else /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ - struct hostent *he; - int i; -#ifdef GG_CONFIG_HAVE_PTHREAD - int old_state; -#endif - - if (result == NULL || count == NULL) { - errno = EINVAL; - return -1; - } - - he = gethostbyname(hostname); - - if (he == NULL || he->h_addr_list[0] == NULL) - return -1; - - /* Policz liczbę adresów */ - - for (i = 0; he->h_addr_list[i] != NULL; i++); - - /* Zaalokuj */ - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); -#endif - - *result = malloc((i + 1) * sizeof(struct in_addr)); - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(old_state, NULL); -#endif - - if (*result == NULL) - return -1; - - /* Kopiuj */ - - for (i = 0; he->h_addr_list[i] != NULL; i++) - memcpy(&((*result)[i]), he->h_addr_list[i], sizeof(struct in_addr)); - - (*result)[i].s_addr = INADDR_NONE; - - *count = i; - - return 0; -#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ -} - -/** - * \internal Rozwiązuje nazwę i zapisuje wynik do podanego gniazda. - * - * \note Użycie logowania w tej funkcji może mieć negatywny wpływ na - * aplikacje jednowątkowe korzystające. - * - * \param fd Deskryptor gniazda - * \param hostname Nazwa serwera - * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -static int gg_resolver_run(int fd, const char *hostname, int pthread) -{ - struct in_addr addr_ip[2], *addr_list = NULL; - unsigned int addr_count; - int res = 0; -#ifdef GG_CONFIG_HAVE_PTHREAD - int old_state; -#endif - -#ifdef GG_CONFIG_HAVE_PTHREAD - pthread_cleanup_push(gg_resolver_cleaner, &addr_list); -#endif - - if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) { - if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, pthread) == -1) { -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); -#endif - - free(addr_list); - addr_list = NULL; - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(old_state, NULL); -#endif - - addr_count = 0; - /* addr_ip[0] już zawiera INADDR_NONE */ - } - } else { - addr_ip[1].s_addr = INADDR_NONE; - addr_count = 1; - } - - if (send(fd, addr_list != NULL ? addr_list : addr_ip, - (addr_count + 1) * sizeof(struct in_addr), 0) != - (int)((addr_count + 1) * sizeof(struct in_addr))) - { - res = -1; - } - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); -#endif - - free(addr_list); - addr_list = NULL; - -#ifdef GG_CONFIG_HAVE_PTHREAD - if (pthread) - pthread_setcancelstate(old_state, NULL); - - pthread_cleanup_pop(0); -#endif - - return res; -} - -/** - * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. - * - * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli - * nie, to zwykłej \c gethostbyname. Funkcja służy do zachowania zgodności - * ABI i służy do pobierania tylko pierwszego adresu -- pozostałe mogą - * zostać zignorowane przez aplikację. - * - * \param hostname Nazwa serwera - * - * \return Zaalokowana struktura \c in_addr lub NULL w przypadku błędu. - */ -struct in_addr *gg_gethostbyname(const char *hostname) -{ - struct in_addr *result; - unsigned int count; - - if (gg_gethostbyname_real(hostname, &result, &count, 0) == -1) - return NULL; - - return result; -} - -#ifdef GG_CONFIG_HAVE_FORK - -/** - * \internal Struktura przekazywana do wątku rozwiązującego nazwę. - */ -struct gg_resolver_fork_data { - int pid; /*< Identyfikator procesu */ -}; - -/** - * \internal Rozwiązuje nazwę serwera w osobnym procesie. - * - * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania - * nazwy serwera. W tym celu tworzona jest para gniazd, nowy proces i dopiero - * w nim przeprowadzane jest rozwiązywanie nazwy. Deskryptor gniazda do odczytu - * zapisuje się w strukturze sieci i czeka na dane w postaci struktury - * \c in_addr. Jeśli nie znaleziono nazwy, zwracana jest \c INADDR_NONE. - * - * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda - * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik - * do numeru procesu potomnego rozwiązującego nazwę - * \param hostname Nazwa serwera do rozwiązania - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname) -{ - struct gg_resolver_fork_data *data = NULL; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); - - if (fd == NULL || priv_data == NULL || hostname == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - data = malloc(sizeof(struct gg_resolver_fork_data)); - - if (data == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n"); - return -1; - } - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable " - "to create pipes (errno=%d, %s)\n", - errno, strerror(errno)); - free(data); - return -1; - } - - data->pid = fork(); - - if (data->pid == -1) { - new_errno = errno; - goto cleanup; - } - - if (data->pid == 0) { - int status; - - close(pipes[0]); - - status = (gg_resolver_run(pipes[1], hostname, 0) == -1) ? 1 : 0; - -#ifdef HAVE__EXIT - _exit(status); -#else - exit(status); -#endif - } - - close(pipes[1]); - - gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data); - - *fd = pipes[0]; - *priv_data = data; - - return 0; - -cleanup: - free(data); - close(pipes[0]); - close(pipes[1]); - - errno = new_errno; - - return -1; -} - -/** - * \internal Usuwanie zasobów po procesie rozwiązywaniu nazwy. - * - * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu - * zasobów sesji podczas rozwiązywania nazwy. - * - * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych - * danych - * \param force Flaga usuwania zasobów przed zakończeniem działania - */ -static void gg_resolver_fork_cleanup(void **priv_data, int force) -{ - struct gg_resolver_fork_data *data; - - if (priv_data == NULL || *priv_data == NULL) - return; - - data = (struct gg_resolver_fork_data*) *priv_data; - *priv_data = NULL; - - if (force) - kill(data->pid, SIGKILL); - - /* we don't care about child's exit status, just want to clean it up */ - (void)waitpid(data->pid, NULL, WNOHANG); - - free(data); -} - -#endif /* GG_CONFIG_HAVE_FORK */ - -#ifdef GG_CONFIG_HAVE_PTHREAD - -/** - * \internal Struktura przekazywana do wątku rozwiązującego nazwę. - */ -struct gg_resolver_pthread_data { - pthread_t thread; /*< Identyfikator wątku */ - char *hostname; /*< Nazwa serwera */ - int wfd; /*< Deskryptor do zapisu */ -}; - -/** - * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy. - * - * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu - * zasobów sesji podczas rozwiązywania nazwy. - * - * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych - * danych - * \param force Flaga usuwania zasobów przed zakończeniem działania - */ -static void gg_resolver_pthread_cleanup(void **priv_data, int force) -{ - struct gg_resolver_pthread_data *data; - - if (priv_data == NULL || *priv_data == NULL) - return; - - data = (struct gg_resolver_pthread_data *) *priv_data; - *priv_data = NULL; - - if (force) - pthread_cancel(data->thread); - - pthread_join(data->thread, NULL); - - close(data->wfd); - free(data->hostname); - free(data); -} - -/** - * \internal Wątek rozwiązujący nazwę. - * - * \param arg Wskaźnik na strukturę \c gg_resolver_pthread_data - */ -static void *gg_resolver_pthread_thread(void *arg) -{ - struct gg_resolver_pthread_data *data = arg; - - if (gg_resolver_run(data->wfd, data->hostname, 1) == -1) - pthread_exit((void*) -1); - else - pthread_exit(NULL); - - return NULL; /* żeby kompilator nie marudził */ -} - -/** - * \internal Rozwiązuje nazwę serwera w osobnym wątku. - * - * Funkcja działa analogicznie do \c gg_resolver_fork_start(), z tą różnicą, - * że działa na wątkach, nie procesach. Jest dostępna wyłącznie gdy podczas - * kompilacji włączono odpowiednią opcję. - * - * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda - * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik - * do prywatnych danych wątku rozwiązującego nazwę - * \param hostname Nazwa serwera do rozwiązania - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname) -{ - struct gg_resolver_pthread_data *data = NULL; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); - - if (fd == NULL || priv_data == NULL || hostname == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - data = malloc(sizeof(struct gg_resolver_pthread_data)); - - if (data == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n"); - return -1; - } - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable " - "to create pipes (errno=%d, %s)\n", - errno, strerror(errno)); - free(data); - return -1; - } - - data->hostname = strdup(hostname); - - if (data->hostname == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - data->wfd = pipes[1]; - - if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, data)) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n"); - new_errno = errno; - goto cleanup; - } - - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data); - - *fd = pipes[0]; - *priv_data = data; - - return 0; - -cleanup: - if (data != NULL) - free(data->hostname); - - free(data); - - close(pipes[0]); - close(pipes[1]); - - errno = new_errno; - - return -1; -} - -#endif /* GG_CONFIG_HAVE_PTHREAD */ - -#ifdef _WIN32 - -/** - * \internal Struktura przekazywana do wątku rozwiązującego nazwę. - */ -struct gg_resolver_win32_data { - HANDLE thread; /*< Uchwyt wątku */ - CRITICAL_SECTION mutex; /*< Semafor wątku */ - char *hostname; /*< Nazwa serwera */ - int wfd; /*< Deskryptor do zapisu */ - int orphan; /*< Wątek powinien sam po sobie posprzątać */ - int finished; /*< Wątek już skończył pracę */ -}; - -/** - * \internal Wątek rozwiązujący nazwę. - * - * \param arg Wskaźnik na strukturę \c gg_resolver_win32_data - */ -static DWORD WINAPI gg_resolver_win32_thread(void *arg) -{ - struct gg_resolver_win32_data *data = arg; - int result, is_orphan; - - result = gg_resolver_run(data->wfd, data->hostname, 0); - - EnterCriticalSection(&data->mutex); - is_orphan = data->orphan; - data->finished = 1; - LeaveCriticalSection(&data->mutex); - - if (is_orphan) { - CloseHandle(data->thread); - DeleteCriticalSection(&data->mutex); - close(data->wfd); - free(data->hostname); - free(data); - } - - ExitThread(result); - return 0; /* żeby kompilator nie marudził */ -} - -/** - * \internal Rozwiązuje nazwę serwera w osobnym wątku. - * - * Funkcja działa analogicznie do \c gg_resolver_pthread_start(), z tą różnicą, - * że działa na wątkach Win32. Jest dostępna wyłącznie przy kompilacji dla - * systemu Windows. - * - * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda - * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik - * do prywatnych danych wątku rozwiązującego nazwę - * \param hostname Nazwa serwera do rozwiązania - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -static int gg_resolver_win32_start(int *fd, void **priv_data, const char *hostname) -{ - struct gg_resolver_win32_data *data = NULL; - int pipes[2], new_errno; - CRITICAL_SECTION *mutex = NULL; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_win32_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); - - if (fd == NULL || priv_data == NULL || hostname == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - data = malloc(sizeof(struct gg_resolver_win32_data)); - - if (data == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() out of memory for resolver data\n"); - return -1; - } - - data->orphan = 0; - data->finished = 0; - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() unable to " - "create pipes (errno=%d, %s)\n", - errno, strerror(errno)); - free(data); - return -1; - } - - data->hostname = strdup(hostname); - - if (data->hostname == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - data->wfd = pipes[1]; - - mutex = &data->mutex; - InitializeCriticalSection(mutex); - - data->thread = CreateThread(NULL, 0, gg_resolver_win32_thread, data, 0, NULL); - if (!data->thread) { - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() unable to create thread\n"); - new_errno = errno; - goto cleanup; - } - - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() %p\n", data); - - *fd = pipes[0]; - *priv_data = data; - - return 0; - -cleanup: - if (data) { - free(data->hostname); - free(data); - } - - close(pipes[0]); - close(pipes[1]); - - if (mutex) - DeleteCriticalSection(mutex); - - errno = new_errno; - - return -1; -} - -/** - * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy. - * - * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu - * zasobów sesji podczas rozwiązywania nazwy. - * - * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych - * danych - * \param force Flaga usuwania zasobów przed zakończeniem działania - */ -static void gg_resolver_win32_cleanup(void **priv_data, int force) -{ - struct gg_resolver_win32_data *data; - - if (priv_data == NULL || *priv_data == NULL) - return; - - data = (struct gg_resolver_win32_data *) *priv_data; - *priv_data = NULL; - - if (WaitForSingleObject(data->thread, 0) == WAIT_TIMEOUT) { - int finished; - /* We cannot call TerminateThread here - it doesn't - * release critical section locks (see MSDN docs). - * if (force) TerminateThread(data->thread, 0); - */ - EnterCriticalSection(&data->mutex); - finished = data->finished; - if (!finished) - data->orphan = 1; - LeaveCriticalSection(&data->mutex); - if (!finished) - return; - } - - CloseHandle(data->thread); - DeleteCriticalSection(&data->mutex); - close(data->wfd); - free(data->hostname); - free(data); -} - -#endif /* _WIN32 */ - -/** - * Ustawia sposób rozwiązywania nazw w sesji. - * - * \param gs Struktura sesji - * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver) - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type) -{ - GG_SESSION_CHECK(gs, -1); - - if (type == GG_RESOLVER_DEFAULT) { - if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) { - gs->resolver_type = gg_global_resolver_type; - gs->resolver_start = gg_global_resolver_start; - gs->resolver_cleanup = gg_global_resolver_cleanup; - return 0; - } - -#ifdef GG_CONFIG_HAVE_PTHREAD - type = GG_RESOLVER_PTHREAD; -#elif defined(_WIN32) - type = GG_RESOLVER_WIN32; -#elif defined(GG_CONFIG_HAVE_FORK) - type = GG_RESOLVER_FORK; -#endif - } - - switch (type) { -#ifdef GG_CONFIG_HAVE_FORK - case GG_RESOLVER_FORK: - gs->resolver_type = type; - gs->resolver_start = gg_resolver_fork_start; - gs->resolver_cleanup = gg_resolver_fork_cleanup; - return 0; -#endif - -#ifdef GG_CONFIG_HAVE_PTHREAD - case GG_RESOLVER_PTHREAD: - gs->resolver_type = type; - gs->resolver_start = gg_resolver_pthread_start; - gs->resolver_cleanup = gg_resolver_pthread_cleanup; - return 0; -#endif - -#ifdef _WIN32 - case GG_RESOLVER_WIN32: - gs->resolver_type = type; - gs->resolver_start = gg_resolver_win32_start; - gs->resolver_cleanup = gg_resolver_win32_cleanup; - return 0; -#endif - - default: - errno = EINVAL; - return -1; - } -} - -/** - * Zwraca sposób rozwiązywania nazw w sesji. - * - * \param gs Struktura sesji - * - * \return Sposób rozwiązywania nazw - */ -gg_resolver_t gg_session_get_resolver(struct gg_session *gs) -{ - GG_SESSION_CHECK(gs, (gg_resolver_t) -1); - - return gs->resolver_type; -} - -/** - * Ustawia własny sposób rozwiązywania nazw w sesji. - * - * \param gs Struktura sesji - * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy - * \param resolver_cleanup Funkcja zwalniająca zasoby - * - * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco: - * - \c "int *fd" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda - * - \c "void **priv_data" — wskaźnik na zmienną, gdzie można umieścić - * wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy - * - \c "const char *name" — nazwa serwera do rozwiązania - * - * Parametry funkcji zwalniającej zasoby wyglądają następująco: - * - \c "void **priv_data" — wskaźnik na zmienną przechowującą wskaźnik - * do prywatnych danych, należy go ustawić na \c NULL po zakończeniu - * - \c "int force" — flaga mówiąca o tym, że zasoby są zwalniane przed - * zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji. - * - * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub - * inny deskryptor pozwalający na co najmniej odbiór danych i przekazać go - * w parametrze \c fd. Na platformie Windows możliwe jest przekazanie jedynie - * deskryptora gniazda. Po zakończeniu rozwiązywania nazwy powinien wysłać - * otrzymany adres IP w postaci sieciowej (big-endian) do deskryptora. Jeśli - * rozwiązywanie nazwy się nie powiedzie, należy wysłać \c INADDR_NONE. - * Następnie zostanie wywołana funkcja zwalniająca zasoby z parametrem - * \c force równym \c 0. Gdyby sesja została zakończona przed rozwiązaniem - * nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja zwalniająca zasoby - * zostanie wywołana z parametrem \c force równym \c 1. - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -int gg_session_set_custom_resolver(struct gg_session *gs, - int (*resolver_start)(int*, void**, const char*), - void (*resolver_cleanup)(void**, int)) -{ - GG_SESSION_CHECK(gs, -1); - - if (resolver_start == NULL || resolver_cleanup == NULL) { - errno = EINVAL; - return -1; - } - - gs->resolver_type = GG_RESOLVER_CUSTOM; - gs->resolver_start = resolver_start; - gs->resolver_cleanup = resolver_cleanup; - - return 0; -} - -/** - * Ustawia sposób rozwiązywania nazw połączenia HTTP. - * - * \param gh Struktura połączenia - * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver) - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type) -{ - if (gh == NULL) { - errno = EINVAL; - return -1; - } - - if (type == GG_RESOLVER_DEFAULT) { - if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) { - gh->resolver_type = gg_global_resolver_type; - gh->resolver_start = gg_global_resolver_start; - gh->resolver_cleanup = gg_global_resolver_cleanup; - return 0; - } - -#ifdef GG_CONFIG_HAVE_PTHREAD - type = GG_RESOLVER_PTHREAD; -#elif defined(_WIN32) - type = GG_RESOLVER_WIN32; -#elif defined(GG_CONFIG_HAVE_FORK) - type = GG_RESOLVER_FORK; -#endif - } - - switch (type) { -#ifdef GG_CONFIG_HAVE_FORK - case GG_RESOLVER_FORK: - gh->resolver_type = type; - gh->resolver_start = gg_resolver_fork_start; - gh->resolver_cleanup = gg_resolver_fork_cleanup; - return 0; -#endif - -#ifdef GG_CONFIG_HAVE_PTHREAD - case GG_RESOLVER_PTHREAD: - gh->resolver_type = type; - gh->resolver_start = gg_resolver_pthread_start; - gh->resolver_cleanup = gg_resolver_pthread_cleanup; - return 0; -#endif - -#ifdef _WIN32 - case GG_RESOLVER_WIN32: - gh->resolver_type = type; - gh->resolver_start = gg_resolver_win32_start; - gh->resolver_cleanup = gg_resolver_win32_cleanup; - return 0; -#endif - - default: - errno = EINVAL; - return -1; - } -} - -/** - * Zwraca sposób rozwiązywania nazw połączenia HTTP. - * - * \param gh Struktura połączenia - * - * \return Sposób rozwiązywania nazw - */ -gg_resolver_t gg_http_get_resolver(struct gg_http *gh) -{ - if (gh == NULL) { - errno = EINVAL; - return GG_RESOLVER_INVALID; - } - - return gh->resolver_type; -} - -/** - * Ustawia własny sposób rozwiązywania nazw połączenia HTTP. - * - * \param gh Struktura sesji - * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy - * \param resolver_cleanup Funkcja zwalniająca zasoby - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -int gg_http_set_custom_resolver(struct gg_http *gh, - int (*resolver_start)(int*, void**, const char*), - void (*resolver_cleanup)(void**, int)) -{ - if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) { - errno = EINVAL; - return -1; - } - - gh->resolver_type = GG_RESOLVER_CUSTOM; - gh->resolver_start = resolver_start; - gh->resolver_cleanup = resolver_cleanup; - - return 0; -} - -/** - * Ustawia sposób rozwiązywania nazw globalnie dla biblioteki. - * - * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver) - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -int gg_global_set_resolver(gg_resolver_t type) -{ - switch (type) { - case GG_RESOLVER_DEFAULT: - gg_global_resolver_type = type; - gg_global_resolver_start = NULL; - gg_global_resolver_cleanup = NULL; - return 0; - -#ifdef GG_CONFIG_HAVE_FORK - case GG_RESOLVER_FORK: - gg_global_resolver_type = type; - gg_global_resolver_start = gg_resolver_fork_start; - gg_global_resolver_cleanup = gg_resolver_fork_cleanup; - return 0; -#endif - -#ifdef GG_CONFIG_HAVE_PTHREAD - case GG_RESOLVER_PTHREAD: - gg_global_resolver_type = type; - gg_global_resolver_start = gg_resolver_pthread_start; - gg_global_resolver_cleanup = gg_resolver_pthread_cleanup; - return 0; -#endif - -#ifdef _WIN32 - case GG_RESOLVER_WIN32: - gg_global_resolver_type = type; - gg_global_resolver_start = gg_resolver_win32_start; - gg_global_resolver_cleanup = gg_resolver_win32_cleanup; - return 0; -#endif - - default: - errno = EINVAL; - return -1; - } -} - -/** - * Zwraca sposób rozwiązywania nazw globalnie dla biblioteki. - * - * \return Sposób rozwiązywania nazw - */ -gg_resolver_t gg_global_get_resolver(void) -{ - return gg_global_resolver_type; -} - -/** - * Ustawia własny sposób rozwiązywania nazw globalnie dla biblioteki. - * - * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy - * \param resolver_cleanup Funkcja zwalniająca zasoby - * - * Patrz \ref gg_session_set_custom_resolver. - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -int gg_global_set_custom_resolver( - int (*resolver_start)(int*, void**, const char*), - void (*resolver_cleanup)(void**, int)) -{ - if (resolver_start == NULL || resolver_cleanup == NULL) { - errno = EINVAL; - return -1; - } - - gg_global_resolver_type = GG_RESOLVER_CUSTOM; - gg_global_resolver_start = resolver_start; - gg_global_resolver_cleanup = resolver_cleanup; - - return 0; -} - -/** - * Odczytuje dane z procesu/wątku rozwiązywania nazw. - * - * \param fd Deskryptor - * \param buf Wskaźnik na bufor - * \param len Długość bufora - * - * \return Patrz recv() i read(). - */ -int gg_resolver_recv(int fd, void *buf, size_t len) -{ -#ifndef _WIN32 - return read(fd, buf, len); -#else - return recv(fd, buf, len, 0); -#endif -}