diff options
Diffstat (limited to 'protocols/Gadu-Gadu/src/libgadu/resolver.cpp')
-rw-r--r-- | protocols/Gadu-Gadu/src/libgadu/resolver.cpp | 229 |
1 files changed, 155 insertions, 74 deletions
diff --git a/protocols/Gadu-Gadu/src/libgadu/resolver.cpp b/protocols/Gadu-Gadu/src/libgadu/resolver.cpp index 9b3ab79147..065169007f 100644 --- a/protocols/Gadu-Gadu/src/libgadu/resolver.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/resolver.cpp @@ -1,6 +1,3 @@ -/* coding: UTF-8 */ -/* $Id$ */ - /* * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> * Robert J. Woźny <speedy@ziew.org> @@ -51,6 +48,7 @@ #include "libgadu.h" #include "resolver.h" #include "compat.h" +#include "session.h" /** Sposób rozwiązywania nazw serwerów */ static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; @@ -88,15 +86,17 @@ static void gg_gethostbyname_cleaner(void *data) * \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. + * 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 addr Wskaźnik na rezultat rozwiązywania nazwy + * \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 *addr, int pthread) +int gg_gethostbyname_real(const char *hostname, struct in_addr **result, int *count, int pthread) { #ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R char *buf = NULL; @@ -104,13 +104,18 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea struct hostent he; struct hostent *he_ptr = NULL; size_t buf_len = 1024; - int result = -1; + 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_gethostbyname_cleaner, &buf); @@ -154,9 +159,41 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea } } - if (ret == 0 && he_ptr != NULL) { - memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr)); - result = 0; + 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) + return -1; + + /* 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; } #ifdef GG_CONFIG_HAVE_PTHREAD @@ -177,26 +214,91 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea pthread_cleanup_pop(1); #endif - return result; -#else + return res; +#else /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ struct hostent *he; + int i; + + if (result == NULL || count == NULL) { + errno = EINVAL; + return -1; + } he = gethostbyname(hostname); - if (he == NULL) + 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 */ + + *result = (in_addr*)malloc((i + 1) * sizeof(struct in_addr)); + + if (*result == NULL) return -1; - memcpy(addr, he->h_addr, sizeof(struct in_addr)); + /* Kopiuj */ + + for (i = 0; he->h_addr_list[i] != NULL; i++) + memcpy(&((*result)[i]), he->h_addr_list[0], 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 desktyptora. + * + * \param fd Deskryptor + * \param hostname Nazwa serwera + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_resolver_run(int fd, const char *hostname) +{ + struct in_addr addr_ip[2], *addr_list; + int addr_count; + int res = 0; + + gg_debug(GG_DEBUG_MISC, "// gg_resolver_run(%d, %s)\n", fd, hostname); + + if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) { + if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 1) == -1) { + addr_list = addr_ip; + /* addr_ip[0] już zawiera INADDR_NONE */ + } + } else { + addr_list = addr_ip; + addr_ip[1].s_addr = INADDR_NONE; + addr_count = 1; + } + + gg_debug(GG_DEBUG_MISC, "// gg_resolver_run() count = %d\n", addr_count); + + if (write(fd, addr_list, (addr_count + 1) * sizeof(struct in_addr)) != (addr_count + 1) * sizeof(struct in_addr)) + res = -1; + + if (addr_list != addr_ip) + free(addr_list); + + 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. + * 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 * @@ -204,16 +306,13 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea */ struct in_addr *gg_gethostbyname(const char *hostname) { - struct in_addr *addr; + struct in_addr *result; + int count; - if (!(addr = (in_addr*)malloc(sizeof(struct in_addr)))) + if (gg_gethostbyname_real(hostname, &result, &count, 0) == -1) return NULL; - if (gg_gethostbyname_real(hostname, addr, 0)) { - free(addr); - return NULL; - } - return addr; + return result; } /** @@ -243,7 +342,6 @@ struct gg_resolver_fork_data { static int gg_resolver_fork_start(SOCKET *fd, void **priv_data, const char *hostname) { struct gg_resolver_fork_data *data = NULL; - struct in_addr addr; int new_errno; SOCKET pipes[2]; @@ -278,16 +376,9 @@ static int gg_resolver_fork_start(SOCKET *fd, void **priv_data, const char *host if (data->pid == 0) { gg_sock_close(pipes[0]); - if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { - /* W przypadku błędu gg_gethostbyname_real() zwróci -1 - * i nie zmieni &addr. Tam jest już INADDR_NONE, - * więc nie musimy robić nic więcej. */ - gg_gethostbyname_real(hostname, &addr, 0); - } - - if (gg_sock_write(pipes[1], &addr, sizeof(addr)) != sizeof(addr)) + if (gg_resolver_run(pipes[1], hostname) == -1) exit(1); - + else exit(0); } @@ -320,7 +411,7 @@ cleanup: * danych * \param force Flaga usuwania zasobów przed zakończeniem działania */ -static void gg_resolver_fork_cleanup(void **priv_data, int force) +void gg_resolver_fork_cleanup(void **priv_data, int force) { struct gg_resolver_fork_data *data; @@ -394,21 +485,13 @@ static void gg_resolver_pthread_cleanup(void **priv_data, int force) static void *__stdcall gg_resolver_pthread_thread(void *arg) { struct gg_resolver_pthread_data *data = (gg_resolver_pthread_data*)arg; - struct in_addr addr; pthread_detach(pthread_self()); - if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) { - /* W przypadku błędu gg_gethostbyname_real() zwróci -1 - * i nie zmieni &addr. Tam jest już INADDR_NONE, - * więc nie musimy robić nic więcej. */ - gg_gethostbyname_real(data->hostname, &addr, 1); - } - - if (gg_sock_write(data->wfd, &addr, sizeof(addr)) == sizeof(addr)) - pthread_exit(NULL); - else + if (gg_resolver_run(data->wfd, data->hostname) == -1) pthread_exit((void*) -1); + else + pthread_exit(NULL); return NULL; /* żeby kompilator nie marudził */ } @@ -480,10 +563,10 @@ static int gg_resolver_pthread_start(SOCKET *fd, void **priv_data, const char *h return 0; cleanup: - if (data) { + if (data != NULL) free(data->hostname); - free(data); - } + + free(data); gg_sock_close(pipes[0]); gg_sock_close(pipes[1]); @@ -505,10 +588,7 @@ cleanup: */ int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type) { - if (gs == NULL) { - errno = EINVAL; - return -1; - } + GG_SESSION_CHECK(gs, -1); if (type == GG_RESOLVER_DEFAULT) { if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) { @@ -555,10 +635,7 @@ int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type) */ gg_resolver_t gg_session_get_resolver(struct gg_session *gs) { - if (gs == NULL) { - errno = EINVAL; - return GG_RESOLVER_INVALID; - } + GG_SESSION_CHECK(gs, (gg_resolver_t) -1); return gs->resolver_type; } @@ -570,11 +647,32 @@ gg_resolver_t gg_session_get_resolver(struct gg_session *gs) * \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 "SOCKET *fd" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku + * - \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 jednostronną komunikację i + * przekazać go w parametrze \c fd. 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)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int)) { - if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) { + GG_SESSION_CHECK(gs, -1); + + if (resolver_start == NULL || resolver_cleanup == NULL) { errno = EINVAL; return -1; } @@ -729,24 +827,7 @@ gg_resolver_t gg_global_get_resolver(void) * \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 "SOCKET *fd" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku - * - \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 jednostronną komunikację i - * przekazać go w parametrze \c fd. 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. + * Patrz \ref gg_session_set_custom_resolver. * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ |