More robust detection of handle and sockets

This commit is contained in:
Werner Koch 2010-04-16 14:08:41 +00:00
parent a684c13c55
commit 779823c09c
2 changed files with 51 additions and 20 deletions

View File

@ -1,3 +1,8 @@
2010-04-16 Werner Koch <wk@g10code.com>
* w32-io.c (is_socket): New.
(reader, writer): Use it to figure out the API to use.
2010-03-15 Werner Koch <wk@g10code.com> 2010-03-15 Werner Koch <wk@g10code.com>
* gpgme.h.in: Add autoconf template to set generated file to * gpgme.h.in: Add autoconf template to set generated file to

View File

@ -1,6 +1,6 @@
/* w32-io.c - W32 API I/O functions. /* w32-io.c - W32 API I/O functions.
Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2000 Werner Koch (dd9jn)
Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010 g10 Code GmbH
This file is part of GPGME. This file is part of GPGME.
@ -175,16 +175,46 @@ set_synchronize (HANDLE hd)
} }
/* Return true if HD refers to a socket. */
static int
is_socket (HANDLE hd)
{
/* We need to figure out whether we are working on a socket or on a
handle. A trivial way would be to check for the return code of
recv and see if it is WSAENOTSOCK. However the recv may block
after the server process died and thus the destroy_reader will
hang. Another option is to use getsockopt to test whether it is
a socket. The bug here is that once a socket with a certain
values has been opened, closed and later a CreatePipe returned
the same value (i.e. handle), getsockopt still believes it is a
socket. What we do now is to use a combination of GetFileType
and GetNamedPipeInfo. The specs say that the latter may be used
on anonymous pipes as well. Note that there are claims that
since winsocket version 2 ReadFile may be used on a socket but
only if it is supported by the service provider. Tests on a
stock XP using a local TCP socket show that it does not work. */
DWORD dummyflags, dummyoutsize, dummyinsize, dummyinst;
if (GetFileType (hd) == FILE_TYPE_PIPE
&& !GetNamedPipeInfo (hd, &dummyflags, &dummyoutsize,
&dummyinsize, &dummyinst))
return 1; /* Function failed; thus we assume it is a socket. */
else
return 0; /* Success; this is not a socket. */
}
static DWORD CALLBACK static DWORD CALLBACK
reader (void *arg) reader (void *arg)
{ {
struct reader_context_s *ctx = arg; struct reader_context_s *ctx = arg;
int nbytes; int nbytes;
DWORD nread; DWORD nread;
int try_recv = 1; int sock;
TRACE_BEG1 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd, TRACE_BEG1 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
"thread=%p", ctx->thread_hd); "thread=%p", ctx->thread_hd);
sock = is_socket (ctx->file_hd);
for (;;) for (;;)
{ {
LOCK (ctx->mutex); LOCK (ctx->mutex);
@ -212,17 +242,15 @@ reader (void *arg)
nbytes = READBUF_SIZE - ctx->writepos; nbytes = READBUF_SIZE - ctx->writepos;
UNLOCK (ctx->mutex); UNLOCK (ctx->mutex);
TRACE_LOG1 ("reading %d bytes", nbytes); TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes);
if (try_recv) if (sock)
{ {
int n; int n;
n = recv (handle_to_socket (ctx->file_hd), n = recv (handle_to_socket (ctx->file_hd),
ctx->buffer + ctx->writepos, nbytes, 0); ctx->buffer + ctx->writepos, nbytes, 0);
if (n < 0 && WSAGetLastError () == WSAENOTSOCK) if (n < 0)
try_recv = 0;
else if (n < 0)
{ {
ctx->error_code = (int) WSAGetLastError (); ctx->error_code = (int) WSAGetLastError ();
if (ctx->error_code == ERROR_BROKEN_PIPE) if (ctx->error_code == ERROR_BROKEN_PIPE)
@ -237,11 +265,9 @@ reader (void *arg)
} }
break; break;
} }
else
nread = n; nread = n;
} }
if (!try_recv) else
{ {
if (!ReadFile (ctx->file_hd, if (!ReadFile (ctx->file_hd,
ctx->buffer + ctx->writepos, nbytes, &nread, NULL)) ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
@ -540,10 +566,12 @@ writer (void *arg)
{ {
struct writer_context_s *ctx = arg; struct writer_context_s *ctx = arg;
DWORD nwritten; DWORD nwritten;
int try_send = 1; int sock;
TRACE_BEG1 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd, TRACE_BEG1 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
"thread=%p", ctx->thread_hd); "thread=%p", ctx->thread_hd);
sock = is_socket (ctx->file_hd);
for (;;) for (;;)
{ {
LOCK (ctx->mutex); LOCK (ctx->mutex);
@ -571,11 +599,12 @@ writer (void *arg)
} }
UNLOCK (ctx->mutex); UNLOCK (ctx->mutex);
TRACE_LOG1 ("writing %d bytes", ctx->nbytes); TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes);
/* Note that CTX->nbytes is not zero at this point, because /* Note that CTX->nbytes is not zero at this point, because
_gpgme_io_write always writes at least 1 byte before waking _gpgme_io_write always writes at least 1 byte before waking
us up, unless CTX->stop_me is true, which we catch above. */ us up, unless CTX->stop_me is true, which we catch above. */
if (try_send) if (sock)
{ {
/* We need to try send first because a socket handle can't /* We need to try send first because a socket handle can't
be used with WriteFile. */ be used with WriteFile. */
@ -583,19 +612,16 @@ writer (void *arg)
n = send (handle_to_socket (ctx->file_hd), n = send (handle_to_socket (ctx->file_hd),
ctx->buffer, ctx->nbytes, 0); ctx->buffer, ctx->nbytes, 0);
if (n < 0 && WSAGetLastError () == WSAENOTSOCK) if (n < 0)
try_send = 0;
else if (n < 0)
{ {
ctx->error_code = (int) WSAGetLastError (); ctx->error_code = (int) WSAGetLastError ();
ctx->error = 1; ctx->error = 1;
TRACE_LOG1 ("send error: ec=%d", ctx->error_code); TRACE_LOG1 ("send error: ec=%d", ctx->error_code);
break; break;
} }
else
nwritten = n; nwritten = n;
} }
if (!try_send) else
{ {
if (!WriteFile (ctx->file_hd, ctx->buffer, if (!WriteFile (ctx->file_hd, ctx->buffer,
ctx->nbytes, &nwritten, NULL)) ctx->nbytes, &nwritten, NULL))