Mercurial > pidgin.yaz
diff libpurple/protocols/gg/lib/resolver.c @ 29944:818f2845ab47
Finish making libgadu compile on Windows. Fixes #10542 finally!
committer: John Bailey <rekkanoryo@rekkanoryo.org>
author | kkszysiu@gmail.com |
---|---|
date | Sat, 06 Mar 2010 16:51:30 +0000 |
parents | db6735e579f8 |
children | eb8b1cfd3349 |
line wrap: on
line diff
--- a/libpurple/protocols/gg/lib/resolver.c Sat Mar 06 16:33:10 2010 +0000 +++ b/libpurple/protocols/gg/lib/resolver.c Sat Mar 06 16:51:30 2010 +0000 @@ -215,6 +215,280 @@ int pid; /*< Identyfikator procesu */ }; + + +#ifdef _WIN32 +/** + * Deal with the fact that you can't select() on a win32 file fd. + * This makes it practically impossible to tie into purple's event loop. + * + * -This is thanks to Tor Lillqvist. + * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. + */ +static int +socket_pipe (int *fds) +{ + SOCKET temp, socket1 = -1, socket2 = -1; + struct sockaddr_in saddr; + int len; + u_long arg; + fd_set read_set, write_set; + struct timeval tv; + + temp = socket(AF_INET, SOCK_STREAM, 0); + + if (temp == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { + goto out0; + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { + goto out0; + } + + if (listen(temp, 1) == SOCKET_ERROR) { + goto out0; + } + + len = sizeof(saddr); + if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { + goto out0; + } + + socket1 = socket(AF_INET, SOCK_STREAM, 0); + + if (socket1 == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out1; + } + + if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || + WSAGetLastError() != WSAEWOULDBLOCK) { + goto out1; + } + + FD_ZERO(&read_set); + FD_SET(temp, &read_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { + goto out1; + } + + if (!FD_ISSET(temp, &read_set)) { + goto out1; + } + + socket2 = accept(temp, (struct sockaddr *) &saddr, &len); + if (socket2 == INVALID_SOCKET) { + goto out1; + } + + FD_ZERO(&write_set); + FD_SET(socket1, &write_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { + goto out2; + } + + if (!FD_ISSET(socket1, &write_set)) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + fds[0] = socket1; + fds[1] = socket2; + + closesocket (temp); + + return 0; + +out2: + closesocket (socket2); +out1: + closesocket (socket1); +out0: + closesocket (temp); + errno = EIO; /* XXX */ + + return -1; +} +#endif + + + +#ifdef _WIN32 +struct gg_resolve_win32thread_data { + char *hostname; + int fd; +}; + +static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) +{ + struct gg_resolve_win32thread_data *d = arg; + struct in_addr a; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() host: %s, fd: %i called\n", d->hostname, d->fd); + + if ((a.s_addr = inet_addr(d->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(d->hostname, &a, 0); + } + + // if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { + // struct in_addr *hn; + + // if (!(hn = gg_gethostbyname(d->hostname))) + // a.s_addr = INADDR_NONE; + // else { + // a.s_addr = hn->s_addr; + // free(hn); + // } + // } + + write(d->fd, &a, sizeof(a)); + close(d->fd); + + free(d->hostname); + d->hostname = NULL; + + free(d); + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() done\n"); + + return 0; +} + + +static int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) +{ + struct gg_resolve_win32thread_data *d = NULL; + HANDLE h; + DWORD dwTId; + int pipes[2], new_errno; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); + + if (!resolver || !fd || !hostname) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + if (socket_pipe(pipes) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); + return -1; + } + + if (!(d = malloc(sizeof(*d)))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + d->hostname = NULL; + + if (!(d->hostname = strdup(hostname))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + d->fd = pipes[1]; + + h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, + d, 0, &dwTId); + + if (h == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); + new_errno = errno; + goto cleanup; + } + + *resolver = h; + *fd = pipes[0]; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() done\n"); + + return 0; + +cleanup: + if (d) { + free(d->hostname); + free(d); + } + + close(pipes[0]); + close(pipes[1]); + + errno = new_errno; + + return -1; + +} + +static void gg_resolve_win32thread_cleanup(void **priv_data, int force) +{ + struct gg_resolve_win32thread_data *data; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force: %i called\n", force); + + if (priv_data == NULL || *priv_data == NULL) + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() priv_data: NULL\n"); + return; + + data = (struct gg_resolve_win32thread_data*) *priv_data; + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() data: %s called\n", data->hostname); + *priv_data = NULL; + + if (force) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force called\n", force); + //pthread_cancel(data->thread); + //pthread_join(data->thread, NULL); + } + + free(data->hostname); + data->hostname = NULL; + + if (data->fd != -1) { + close(data->fd); + data->fd = -1; + } + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() done\n"); + free(data); +} +#endif + +#ifndef _WIN32 /** * \internal Rozwiązuje nazwę serwera w osobnym procesie. * @@ -328,6 +602,7 @@ free(data); } +#endif #ifdef GG_CONFIG_HAVE_PTHREAD @@ -508,19 +783,31 @@ return 0; } -#if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) +#ifdef _WIN32 + type = GG_RESOLVER_WIN32; +#else type = GG_RESOLVER_FORK; -#else +#endif + +#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT) type = GG_RESOLVER_PTHREAD; #endif } switch (type) { - case GG_RESOLVER_FORK: +#ifdef _WIN32 + case GG_RESOLVER_WIN32: + gs->resolver_type = type; + gs->resolver_start = gg_resolve_win32thread; + gs->resolver_cleanup = gg_resolve_win32thread_cleanup; + return 0; +#else + 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: @@ -599,19 +886,31 @@ return 0; } -#if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) +#ifdef _WIN32 + type = GG_RESOLVER_WIN32; +#else type = GG_RESOLVER_FORK; -#else +#endif + +#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT) type = GG_RESOLVER_PTHREAD; #endif } switch (type) { - case GG_RESOLVER_FORK: +#ifdef _WIN32 + case GG_RESOLVER_WIN32: + gh->resolver_type = type; + gh->resolver_start = gg_resolve_win32thread; + gh->resolver_cleanup = gg_resolve_win32thread_cleanup; + return 0; +#else + 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: @@ -683,12 +982,22 @@ gg_global_resolver_cleanup = NULL; return 0; +#ifndef _WIN32 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 _WIN32 + case GG_RESOLVER_WIN32: + gg_global_resolver_type = type; + gg_global_resolver_start = gg_resolve_win32thread; + gg_global_resolver_cleanup = gg_resolve_win32thread_cleanup; + return 0; +#endif + #ifdef GG_CONFIG_HAVE_PTHREAD case GG_RESOLVER_PTHREAD: gg_global_resolver_type = type;