On Sun, Apr  6, 2014 at 11:45:59AM +0530, Amit Kapila wrote:
> On Tue, Apr 1, 2014 at 6:31 AM, Bruce Momjian <br...@momjian.us> wrote:
> > I reviewed this patch and you are correct that we are not handling
> > socket() and accept() returns properly on Windows.  We were doing it
> > properly in most place in the backend, but your patch fixes the
> > remaining places:
> >
> >         
> > http://msdn.microsoft.com/en-us/library/windows/desktop/ms740516%28v=vs.85%29.aspx
> >
> > However, libpq doesn't seem to be doing much to handle Windows properly
> > in this area.  I have adjusted libpq to map socket to -1, but the proper
> > fix is to distribute pgsocket and PGINVALID_SOCKET checks throughout the
> > libpq code.  I am not sure how to handle PQsocket() --- should it still
> > return -1?
> 
> I think changing PQsocket() can impact all existing applications as
> it is mentioned in docs that "result of -1 indicates that no server
> connection is currently open.".  Do you see any compelling need to
> change return value of PQSocket() after your patch?

No, I do not.  In fact, the SSL_get_fd() call in secure_read() returns a
signed integer too, and that is passed around like a socket, so in fact
the SSL API doesn't even allow us to get an unsigned value on Windows in
all cases.

> > Having the return value be conditional on the operating
> > system is ugly.  How much of this should be backpatched?
> 
> I think it's okay to back patch all the changes.
> Is there any part in patch which you feel is risky to back patch?

Well, we would not backpatch this if it is just a stylistic fix, and I
am starting to think it just a style issue.  This MSDN website says -1,
SOCKET_ERROR, and INVALID_SOCKET are very similar:

        
http://msdn.microsoft.com/en-us/library/windows/desktop/cc507522%28v=vs.85%29.aspx

and this Stackoverflow thread says the same:

        
http://stackoverflow.com/questions/10817252/why-is-invalid-socket-defined-as-0-in-winsock2-h-c

In fact, this C program compiled by gcc on Debian issues no compiler
warnings and returns 'hello', showing that -1 and ~0 compare as equal:

        int
        main(int argc, char **argv)
        {
            int i;
            unsigned int j;
        
            i = -1;
            j = ~0;
        
            if (i == j)
                printf("hello\n");
        
            return 0;
        }

meaning our incorrect syntax is computed correctly.

> >  Why aren't we
> > getting warnings on Windows about assigning the socket() return value to
> > an integer?
> 
> I think by default Windows doesn't give warning for such code even at Warning
> level 4.  I have found one related link:
> http://stackoverflow.com/questions/75385/make-vs-compiler-catch-signed-unsigned-assignments
> 
> > Updated patch attached.
> 
> It seems you have missed to change at below places.
> 
> 1.
> int
> pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
> sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
> if (sock == SOCKET_ERROR)

Well, the actual problem here is that WSASocket() returns INVALID_SOCKET
per the documentation, not SOCKET_ERROR.  I did not use PGINVALID_SOCKET
here because this is Windows-specific code, defining 'sock' as SOCKET. 
We could have sock defined as pgsocket, but because this is Windows code
already, it doesn't seem wise to mix portability code in there.

> 2.
> pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout)
> {
> static HANDLE waitevent = INVALID_HANDLE_VALUE;
> static SOCKET current_socket = -1;

Yes, that -1 is wrong and I have changed it to INVALID_SOCKET, again
using the same rules that say PGINVALID_SOCKET doesn't make sense here
as it is Windows-specific code.

I am attaching an updated patch, which explains the PQsocket() return
value issue, and fixes the items listed above.  I am inclined to apply
this just to head for correctness, and modify libpq to use pgsocket
consistently in a follow-up patch.

This is not like the readdir() fix we had to backpatch because that was
clearly not catching errors.

-- 
  Bruce Momjian  <br...@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + Everyone has their own god. +
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
new file mode 100644
index d062c1d..8fa9aa7
*** a/src/backend/libpq/auth.c
--- b/src/backend/libpq/auth.c
*************** ident_inet(hbaPort *port)
*** 1463,1469 ****
  
  	sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
  					 ident_serv->ai_protocol);
! 	if (sock_fd < 0)
  	{
  		ereport(LOG,
  				(errcode_for_socket_access(),
--- 1463,1469 ----
  
  	sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
  					 ident_serv->ai_protocol);
! 	if (sock_fd == PGINVALID_SOCKET)
  	{
  		ereport(LOG,
  				(errcode_for_socket_access(),
*************** ident_inet(hbaPort *port)
*** 1543,1549 ****
  					ident_response)));
  
  ident_inet_done:
! 	if (sock_fd >= 0)
  		closesocket(sock_fd);
  	pg_freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
  	pg_freeaddrinfo_all(local_addr.addr.ss_family, la);
--- 1543,1549 ----
  					ident_response)));
  
  ident_inet_done:
! 	if (sock_fd != PGINVALID_SOCKET)
  		closesocket(sock_fd);
  	pg_freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
  	pg_freeaddrinfo_all(local_addr.addr.ss_family, la);
*************** CheckRADIUSAuth(Port *port)
*** 2361,2367 ****
  	packet->length = htons(packet->length);
  
  	sock = socket(serveraddrs[0].ai_family, SOCK_DGRAM, 0);
! 	if (sock < 0)
  	{
  		ereport(LOG,
  				(errmsg("could not create RADIUS socket: %m")));
--- 2361,2367 ----
  	packet->length = htons(packet->length);
  
  	sock = socket(serveraddrs[0].ai_family, SOCK_DGRAM, 0);
! 	if (sock == PGINVALID_SOCKET)
  	{
  		ereport(LOG,
  				(errmsg("could not create RADIUS socket: %m")));
diff --git a/src/backend/libpq/ip.c b/src/backend/libpq/ip.c
new file mode 100644
index 7e8fc78..acdbab0
*** a/src/backend/libpq/ip.c
--- b/src/backend/libpq/ip.c
*************** pg_foreach_ifaddr(PgIfAddrCallback callb
*** 547,553 ****
  	int			error;
  
  	sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
! 	if (sock == SOCKET_ERROR)
  		return -1;
  
  	while (n_ii < 1024)
--- 547,553 ----
  	int			error;
  
  	sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
! 	if (sock == INVALID_SOCKET)
  		return -1;
  
  	while (n_ii < 1024)
*************** pg_foreach_ifaddr(PgIfAddrCallback callb
*** 670,676 ****
  				total;
  
  	sock = socket(AF_INET, SOCK_DGRAM, 0);
! 	if (sock == -1)
  		return -1;
  
  	while (n_buffer < 1024 * 100)
--- 670,676 ----
  				total;
  
  	sock = socket(AF_INET, SOCK_DGRAM, 0);
! 	if (sock == PGINVALID_SOCKET)
  		return -1;
  
  	while (n_buffer < 1024 * 100)
*************** pg_foreach_ifaddr(PgIfAddrCallback callb
*** 711,717 ****
  #ifdef HAVE_IPV6
  	/* We'll need an IPv6 socket too for the SIOCGLIFNETMASK ioctls */
  	sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
! 	if (sock6 == -1)
  	{
  		free(buffer);
  		close(sock);
--- 711,717 ----
  #ifdef HAVE_IPV6
  	/* We'll need an IPv6 socket too for the SIOCGLIFNETMASK ioctls */
  	sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
! 	if (sock6 == PGINVALID_SOCKET)
  	{
  		free(buffer);
  		close(sock);
*************** pg_foreach_ifaddr(PgIfAddrCallback callb
*** 788,797 ****
  	char	   *ptr,
  			   *buffer = NULL;
  	size_t		n_buffer = 1024;
! 	int			sock;
  
  	sock = socket(AF_INET, SOCK_DGRAM, 0);
! 	if (sock == -1)
  		return -1;
  
  	while (n_buffer < 1024 * 100)
--- 788,797 ----
  	char	   *ptr,
  			   *buffer = NULL;
  	size_t		n_buffer = 1024;
! 	pgsocket	sock;
  
  	sock = socket(AF_INET, SOCK_DGRAM, 0);
! 	if (sock == PGINVALID_SOCKET)
  		return -1;
  
  	while (n_buffer < 1024 * 100)
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
new file mode 100644
index 1eae183..0179451
*** a/src/backend/libpq/pqcomm.c
--- b/src/backend/libpq/pqcomm.c
*************** StreamServerPort(int family, char *hostN
*** 392,398 ****
  				break;
  		}
  
! 		if ((fd = socket(addr->ai_family, SOCK_STREAM, 0)) < 0)
  		{
  			ereport(LOG,
  					(errcode_for_socket_access(),
--- 392,398 ----
  				break;
  		}
  
! 		if ((fd = socket(addr->ai_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
  		{
  			ereport(LOG,
  					(errcode_for_socket_access(),
*************** StreamConnection(pgsocket server_fd, Por
*** 632,638 ****
  	port->raddr.salen = sizeof(port->raddr.addr);
  	if ((port->sock = accept(server_fd,
  							 (struct sockaddr *) & port->raddr.addr,
! 							 &port->raddr.salen)) < 0)
  	{
  		ereport(LOG,
  				(errcode_for_socket_access(),
--- 632,638 ----
  	port->raddr.salen = sizeof(port->raddr.addr);
  	if ((port->sock = accept(server_fd,
  							 (struct sockaddr *) & port->raddr.addr,
! 							 &port->raddr.salen)) == PGINVALID_SOCKET)
  	{
  		ereport(LOG,
  				(errcode_for_socket_access(),
diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
new file mode 100644
index 4f1099f..adc0e02
*** a/src/backend/port/win32/socket.c
--- b/src/backend/port/win32/socket.c
*************** int
*** 132,138 ****
  pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout)
  {
  	static HANDLE waitevent = INVALID_HANDLE_VALUE;
! 	static SOCKET current_socket = -1;
  	static int	isUDP = 0;
  	HANDLE		events[2];
  	int			r;
--- 132,138 ----
  pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout)
  {
  	static HANDLE waitevent = INVALID_HANDLE_VALUE;
! 	static SOCKET current_socket = INVALID_SOCKET;
  	static int	isUDP = 0;
  	HANDLE		events[2];
  	int			r;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
new file mode 100644
index bb771a6..b573fd8
*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
*************** ConnCreate(int serverFd)
*** 2169,2175 ****
  
  	if (StreamConnection(serverFd, port) != STATUS_OK)
  	{
! 		if (port->sock >= 0)
  			StreamClose(port->sock);
  		ConnFree(port);
  		return NULL;
--- 2169,2175 ----
  
  	if (StreamConnection(serverFd, port) != STATUS_OK)
  	{
! 		if (port->sock != PGINVALID_SOCKET)
  			StreamClose(port->sock);
  		ConnFree(port);
  		return NULL;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
new file mode 100644
index d53c41f..459e4a4
*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
*************** keep_going:						/* We will come back to
*** 1632,1639 ****
  					conn->raddr.salen = addr_cur->ai_addrlen;
  
  					/* Open a socket */
! 					conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
! 					if (conn->sock < 0)
  					{
  						/*
  						 * ignore socket() failure if we have more addresses
--- 1632,1654 ----
  					conn->raddr.salen = addr_cur->ai_addrlen;
  
  					/* Open a socket */
! 					{
! 						/*
! 						 * While we use 'pgsocket' as the socket type in the
! 						 * backend, we use 'int' for libpq socket values.
! 						 * This requires us to map PGINVALID_SOCKET to -1
! 						 * on Windows.
! 						 * See http://msdn.microsoft.com/en-us/library/windows/desktop/ms740516%28v=vs.85%29.aspx
! 						 */
! 						pgsocket sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
! #ifdef WIN32
! 						if (sock == PGINVALID_SOCKET)
! 							conn->sock = -1;
! 						else
! #endif
! 							conn->sock = sock;
! 					}
! 					if (conn->sock == -1)
  					{
  						/*
  						 * ignore socket() failure if we have more addresses
*************** internal_cancel(SockAddr *raddr, int be_
*** 3136,3142 ****
  				char *errbuf, int errbufsize)
  {
  	int			save_errno = SOCK_ERRNO;
! 	int			tmpsock = -1;
  	char		sebuf[256];
  	int			maxlen;
  	struct
--- 3151,3157 ----
  				char *errbuf, int errbufsize)
  {
  	int			save_errno = SOCK_ERRNO;
! 	pgsocket	tmpsock = PGINVALID_SOCKET;
  	char		sebuf[256];
  	int			maxlen;
  	struct
*************** internal_cancel(SockAddr *raddr, int be_
*** 3149,3155 ****
  	 * We need to open a temporary connection to the postmaster. Do this with
  	 * only kernel calls.
  	 */
! 	if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) < 0)
  	{
  		strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
  		goto cancel_errReturn;
--- 3164,3170 ----
  	 * We need to open a temporary connection to the postmaster. Do this with
  	 * only kernel calls.
  	 */
! 	if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
  	{
  		strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
  		goto cancel_errReturn;
*************** cancel_errReturn:
*** 3220,3226 ****
  				maxlen);
  		strcat(errbuf, "\n");
  	}
! 	if (tmpsock >= 0)
  		closesocket(tmpsock);
  	SOCK_ERRNO_SET(save_errno);
  	return FALSE;
--- 3235,3241 ----
  				maxlen);
  		strcat(errbuf, "\n");
  	}
! 	if (tmpsock != PGINVALID_SOCKET)
  		closesocket(tmpsock);
  	SOCK_ERRNO_SET(save_errno);
  	return FALSE;
*************** PQerrorMessage(const PGconn *conn)
*** 5300,5308 ****
--- 5315,5333 ----
  	return conn->errorMessage.data;
  }
  
+ /*
+  * In Windows, socket values are unsigned, and an invalid socket value
+  * (INVALID_SOCKET) is ~0, which equals -1 in comparisons (with no compiler
+  * warning). Ideally we would return an unsigned value for PQsocket() on
+  * Windows, but that would cause the function's return value to differ from
+  * Unix, so we just return -1 for invalid sockets.
+  * http://msdn.microsoft.com/en-us/library/windows/desktop/cc507522%28v=vs.85%29.aspx
+  * http://stackoverflow.com/questions/10817252/why-is-invalid-socket-defined-as-0-in-winsock2-h-c
+  */
  int
  PQsocket(const PGconn *conn)
  {
+ 	
  	if (!conn)
  		return -1;
  	return conn->sock;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
new file mode 100644
index 22bbe4a..ee975d4
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
*************** struct pg_conn
*** 364,369 ****
--- 364,370 ----
  	PGnotify   *notifyTail;		/* newest unreported Notify msg */
  
  	/* Connection data */
+ 	/* See PQconnectPoll() for how we use 'int' and not 'pgsocket'. */
  	int			sock;			/* Unix FD for socket, -1 if not connected */
  	SockAddr	laddr;			/* Local address */
  	SockAddr	raddr;			/* Remote address */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to