summaryrefslogtreecommitdiff
path: root/protocols/Gadu-Gadu/src/libgadu/resolver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Gadu-Gadu/src/libgadu/resolver.cpp')
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/resolver.cpp229
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" &mdash; wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku
+ * - \c "void **priv_data" &mdash; wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy
+ * - \c "const char *name" &mdash; nazwa serwera do rozwiązania
+ *
+ * Parametry funkcji zwalniającej zasoby wyglądają następująco:
+ * - \c "void **priv_data" &mdash; wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu
+ * - \c "int force" &mdash; 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" &mdash; wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku
- * - \c "void **priv_data" &mdash; wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy
- * - \c "const char *name" &mdash; nazwa serwera do rozwiązania
- *
- * Parametry funkcji zwalniającej zasoby wyglądają następująco:
- * - \c "void **priv_data" &mdash; wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu
- * - \c "int force" &mdash; 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
*/