1 /* socket.c: Socket-related compatibility routines
2    Copyright (c) 2011 Sergio Baldoví, Philip Kendall
3 
4    $Id: socket.c 4775 2012-11-26 23:03:36Z sbaldovi $
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20    Author contact information:
21 
22    E-mail: philip-fuse@shadowmagic.org.uk
23 
24 */
25 
26 #include <config.h>
27 
28 #include <winsock2.h>
29 #include <ws2tcpip.h>
30 
31 #include "compat.h"
32 #include "fuse.h"
33 #include "ui/ui.h"
34 
35 const compat_socket_t compat_socket_invalid = INVALID_SOCKET;
36 const int compat_socket_EBADF = WSAENOTSOCK;
37 
38 struct compat_socket_selfpipe_t {
39   SOCKET self_socket;
40   libspectrum_word port;
41 };
42 
43 int
compat_socket_close(compat_socket_t fd)44 compat_socket_close( compat_socket_t fd )
45 {
46   return closesocket( fd );
47 }
48 
compat_socket_get_error(void)49 int compat_socket_get_error( void )
50 {
51   return WSAGetLastError();
52 }
53 
54 const char *
compat_socket_get_strerror(void)55 compat_socket_get_strerror( void )
56 {
57   static TCHAR buffer[256];
58   TCHAR *ptr;
59   DWORD msg_size;
60 
61   /* get description of last winsock error */
62   msg_size = FormatMessage(
63                FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
64                NULL, WSAGetLastError(),
65                MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
66                buffer, sizeof( buffer ) / sizeof( TCHAR ), NULL );
67 
68   if( !msg_size ) return NULL;
69 
70   /* skip 'new line' like chars */
71   for( ptr = buffer; *ptr; ptr++ ) {
72     if( ( *ptr == '\r' ) || ( *ptr == '\n' ) ) {
73       *ptr = '\0';
74       break;
75     }
76   }
77 
78   return (const char *)buffer;
79 }
80 
81 static int
selfpipe_test(compat_socket_selfpipe_t * self)82 selfpipe_test( compat_socket_selfpipe_t *self )
83 {
84   fd_set readfds;
85   int active;
86   struct timeval tv = { 1, 0 };
87 
88   /* Send testing packet */
89   compat_socket_selfpipe_wake( self );
90 
91   /* Safe reading from control socket */
92   FD_ZERO( &readfds );
93   FD_SET( self->self_socket, &readfds );
94   active = select( 0, &readfds, NULL, NULL, &tv );
95   if( active == 0 || active == compat_socket_invalid ) {
96     return -1;
97   }
98 
99   /* Discard testing packet */
100   if( FD_ISSET( self->self_socket, &readfds ) ) {
101     compat_socket_selfpipe_discard_data( self );
102   }
103 
104   return 0;
105 }
106 
107 compat_socket_selfpipe_t *
compat_socket_selfpipe_alloc(void)108 compat_socket_selfpipe_alloc( void )
109 {
110   unsigned long mode = 1;
111   struct sockaddr_in sa;
112   socklen_t sa_len = sizeof(sa);
113 
114   compat_socket_selfpipe_t *self = malloc( sizeof( *self ) );
115   if( !self ) {
116     ui_error( UI_ERROR_ERROR, "%s: %d: out of memory", __FILE__, __LINE__ );
117     fuse_abort();
118   }
119 
120   self->self_socket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
121   if( self->self_socket == compat_socket_invalid ) {
122     ui_error( UI_ERROR_ERROR,
123               "%s: %d: failed to open socket; errno %d: %s\n",
124               __FILE__, __LINE__, compat_socket_get_error(),
125               compat_socket_get_strerror() );
126     fuse_abort();
127   }
128 
129   /* Set nonblocking mode */
130   if( ioctlsocket( self->self_socket, FIONBIO, &mode ) == -1 ) {
131     ui_error( UI_ERROR_ERROR,
132               "%s: %d: failed to set socket nonblocking; errno %d: %s\n",
133               __FILE__, __LINE__, compat_socket_get_error(),
134               compat_socket_get_strerror() );
135     fuse_abort();
136   }
137 
138   memset( &sa, 0, sizeof(sa) );
139   sa.sin_family = AF_INET;
140   sa.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
141 
142   if( bind( self->self_socket, (struct sockaddr*)&sa, sizeof(sa) ) == -1 ) {
143     ui_error( UI_ERROR_ERROR,
144               "%s: %d: failed to bind socket; errno %d: %s\n",
145               __FILE__, __LINE__, compat_socket_get_error(),
146               compat_socket_get_strerror() );
147     fuse_abort();
148   }
149 
150   /* Get ephemeral port number */
151   if( getsockname( self->self_socket, (struct sockaddr *)&sa, &sa_len ) == -1 ) {
152     ui_error( UI_ERROR_ERROR,
153               "%s: %d: failed to get socket name; errno %d: %s\n",
154               __FILE__, __LINE__, compat_socket_get_error(),
155               compat_socket_get_strerror() );
156     fuse_abort();
157   }
158 
159   self->port = ntohs( sa.sin_port );
160 
161   /* Test communications in order to detect blocking firewalls */
162   if( selfpipe_test( self ) == -1 ) {
163     ui_error( UI_ERROR_ERROR,
164               "Networking: failed to test internal communications" );
165     fuse_abort();
166   }
167 
168   return self;
169 }
170 
171 void
compat_socket_selfpipe_free(compat_socket_selfpipe_t * self)172 compat_socket_selfpipe_free( compat_socket_selfpipe_t *self )
173 {
174   compat_socket_close( self->self_socket );
175   free( self );
176 }
177 
178 compat_socket_t
compat_socket_selfpipe_get_read_fd(compat_socket_selfpipe_t * self)179 compat_socket_selfpipe_get_read_fd( compat_socket_selfpipe_t *self )
180 {
181   return self->self_socket;
182 }
183 
184 void
compat_socket_selfpipe_wake(compat_socket_selfpipe_t * self)185 compat_socket_selfpipe_wake( compat_socket_selfpipe_t *self )
186 {
187   struct sockaddr_in sa;
188 
189   memset( &sa, 0, sizeof(sa) );
190   sa.sin_family = AF_INET;
191   sa.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
192   sa.sin_port = htons( self->port );
193 
194   sendto( self->self_socket, NULL, 0, 0, (struct sockaddr*)&sa, sizeof(sa) );
195 }
196 
197 void
compat_socket_selfpipe_discard_data(compat_socket_selfpipe_t * self)198 compat_socket_selfpipe_discard_data( compat_socket_selfpipe_t *self )
199 {
200   ssize_t bytes_read;
201   struct sockaddr_in sa;
202   socklen_t sa_length = sizeof(sa);
203   static char bitbucket[0x100];
204 
205   do {
206     /* Socket is non blocking, so we can do this safely */
207     bytes_read = recvfrom( self->self_socket, bitbucket, sizeof( bitbucket ),
208                            0, (struct sockaddr*)&sa, &sa_length );
209   } while( bytes_read != -1 );
210 }
211 
212 
213 void
compat_socket_networking_init(void)214 compat_socket_networking_init( void )
215 {
216   WORD wVersionRequested;
217   WSADATA wsaData;
218   int error;
219 
220   wVersionRequested = MAKEWORD( 2, 2 );
221   error = WSAStartup( wVersionRequested, &wsaData );
222   if( error ) {
223     ui_error( UI_ERROR_ERROR, "%s:%d: error %d from WSAStartup()", __FILE__,
224               __LINE__, error );
225     fuse_abort();
226   }
227 
228   if( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) {
229     ui_error( UI_ERROR_ERROR,
230               "%s:%d: unexpected version 0x%02x from WSAStartup()",
231               __FILE__, __LINE__, wsaData.wVersion );
232     fuse_abort();
233   }
234 }
235 
236 void
compat_socket_networking_end(void)237 compat_socket_networking_end( void )
238 {
239   WSACleanup();
240 }
241 
242