1 /*
2    Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 
26 #include <ndb_global.h>
27 #include <NdbTCP.h>
28 
29 
30 /* On some operating systems (e.g. Solaris) INADDR_NONE is not defined */
31 #ifndef INADDR_NONE
32 #define INADDR_NONE -1                          /* Error value from inet_addr */
33 #endif
34 
35 
36 extern "C"
37 int
Ndb_getInAddr(struct in_addr * dst,const char * address)38 Ndb_getInAddr(struct in_addr * dst, const char *address)
39 {
40   struct addrinfo hints;
41   memset(&hints, 0, sizeof(hints));
42   hints.ai_family = AF_INET; // Only IPv4 address
43   hints.ai_socktype = SOCK_STREAM;
44   hints.ai_protocol = IPPROTO_TCP;
45 
46   struct addrinfo* ai_list;
47   if (getaddrinfo(address, NULL, &hints, &ai_list) != 0)
48   {
49     dst->s_addr = INADDR_NONE;
50     return -1;
51   }
52 
53   /* Return sin_addr for the first address returned */
54   struct sockaddr_in* sin = (struct sockaddr_in*)ai_list->ai_addr;
55   memcpy(dst, &sin->sin_addr, sizeof(struct in_addr));
56 
57   freeaddrinfo(ai_list);
58   return 0;
59 }
60 
61 #ifdef TEST_NDBGETINADDR
62 #include <NdbTap.hpp>
63 
64 static void
CHECK(const char * address,int expected_res,bool is_numeric=false)65 CHECK(const char* address, int expected_res, bool is_numeric= false)
66 {
67   struct in_addr addr;
68 
69   fprintf(stderr, "Testing '%s'\n", address);
70 
71   int res= Ndb_getInAddr(&addr, address);
72 
73   if (res != expected_res)
74   {
75     fprintf(stderr, "> unexpected result: %d, expected: %d\n",
76             res, expected_res);
77     abort();
78   }
79 
80   if (res != 0)
81   {
82     fprintf(stderr, "> returned -1, checking INADDR_NONE\n");
83 
84     // Should return INADDR_NONE when when lookup fails
85     struct in_addr none;
86     none.s_addr = INADDR_NONE;
87     if (memcmp(&addr, &none, sizeof(none)) != 0)
88     {
89       fprintf(stderr, "> didn't return INADDR_NONE after failure, "
90              "got: '%s', expected; '%s'\n",
91              inet_ntoa(addr), inet_ntoa(none));
92       abort();
93     }
94     fprintf(stderr, "> ok\n");
95     return;
96   }
97 
98   fprintf(stderr, "> '%s' -> '%s'\n", address, inet_ntoa(addr));
99 
100   if (is_numeric)
101   {
102     // Check that numeric address always map back to itself
103     // ie. compare to value returned by 'inet_aton'
104     fprintf(stderr, "> Checking numeric address against inet_addr\n");
105     struct in_addr addr2;
106     addr2.s_addr = inet_addr(address);
107     fprintf(stderr, "> inet_addr(%s) -> '%s'\n", address, inet_ntoa(addr2));
108 
109     if (memcmp(&addr, &addr2, sizeof(struct in_addr)) != 0)
110     {
111       fprintf(stderr, "> numeric address '%s' didn't map to same value as "
112               "inet_addr: '%s'", address, inet_ntoa(addr2));
113       abort();
114     }
115     fprintf(stderr, "> ok\n");
116   }
117 }
118 
119 
120 /*
121   socket_library_init
122    - Normally done by ndb_init(), but to avoid
123      having to link with "everything", implement it locally
124 */
125 
126 static void
socket_library_init(void)127 socket_library_init(void)
128 {
129 #ifdef _WIN32
130   WORD requested_version = MAKEWORD( 2, 0 );
131   WSADATA wsa_data;
132   if (WSAStartup( requested_version, &wsa_data ))
133   {
134     fprintf(stderr, "failed to init Winsock\n");
135     abort();
136   }
137 
138   // Confirm that the requested version of the library was loaded
139   if (wsa_data.wVersion != requested_version)
140   {
141     (void)WSACleanup();
142     fprintf(stderr, "Wrong version of Winsock loaded\n");
143     abort();
144   }
145 #endif
146 }
147 
148 
149 static void
socket_library_end()150 socket_library_end()
151 {
152 #ifdef _WIN32
153   (void)WSACleanup();
154 #endif
155 }
156 
157 static bool
can_resolve_hostname(const char * name)158 can_resolve_hostname(const char* name)
159 {
160   fprintf(stderr, "Checking if '%s' can be used for testing\n", name);
161   struct addrinfo hints;
162   memset(&hints, 0, sizeof(hints));
163   hints.ai_family = AF_INET; // Only IPv4 address
164   hints.ai_socktype = SOCK_STREAM;
165   hints.ai_protocol = IPPROTO_TCP;
166 
167   struct addrinfo* ai_list;
168   int err = getaddrinfo(name, NULL, &hints, &ai_list);
169 
170   if (err)
171   {
172     fprintf(stderr, "> '%s' -> error: %d '%s'\n",
173              name, err, gai_strerror(err));
174 
175     if (err == EAI_NODATA ||
176 	err == EAI_NONAME)
177     {
178       // An OK error
179       fprintf(stderr, ">  skipping tests with this name...\n");
180       return false;
181     }
182 
183     // Another unhandled error
184     abort();
185   }
186 
187   freeaddrinfo(ai_list);
188 
189   return true;
190 }
191 
192 
TAPTEST(NdbGetInAddr)193 TAPTEST(NdbGetInAddr)
194 {
195   socket_library_init();
196 
197   if (can_resolve_hostname("localhost"))
198     CHECK("localhost", 0);
199   CHECK("127.0.0.1", 0, true);
200 
201   char hostname_buf[256];
202   if (gethostname(hostname_buf, sizeof(hostname_buf)) == 0 &&
203       can_resolve_hostname(hostname_buf))
204   {
205     // Check this machines hostname
206     CHECK(hostname_buf, 0);
207 
208     struct in_addr addr;
209     Ndb_getInAddr(&addr, hostname_buf);
210     // Convert hostname to dotted decimal string ip and check
211     CHECK(inet_ntoa(addr), 0, true);
212   }
213   CHECK("unknown_?host", -1); // Does not exist
214   CHECK("3ffe:1900:4545:3:200:f8ff:fe21:67cf", -1); // No IPv6
215   CHECK("fe80:0:0:0:200:f8ff:fe21:67cf", -1);
216   CHECK("fe80::200:f8ff:fe21:67cf", -1);
217   CHECK("::1", -1); // the loopback, but still No IPv6
218 
219   socket_library_end();
220 
221   return 1; // OK
222 }
223 #endif
224 
225 
226 static inline
my_socket_nfds(ndb_socket_t s,int nfds)227 int my_socket_nfds(ndb_socket_t s, int nfds)
228 {
229 #ifdef _WIN32
230   (void)s;
231 #else
232   if(s.fd > nfds)
233     return s.fd;
234 #endif
235   return nfds;
236 }
237 
238 #define my_FD_SET(sock,set)   FD_SET(ndb_socket_get_native(sock), set)
239 #define my_FD_ISSET(sock,set) FD_ISSET(ndb_socket_get_native(sock), set)
240 
241 
Ndb_check_socket_hup(NDB_SOCKET_TYPE sock)242 int Ndb_check_socket_hup(NDB_SOCKET_TYPE sock)
243 {
244 #ifdef HAVE_POLL
245   struct pollfd pfd[1];
246   int r;
247 
248   pfd[0].fd= sock.fd; // FIXME: THIS IS A BUG
249   pfd[0].events= POLLHUP | POLLIN | POLLOUT | POLLNVAL;
250   pfd[0].revents= 0;
251   r= poll(pfd,1,0);
252   if(pfd[0].revents & (POLLHUP|POLLERR))
253     return 1;
254 
255   return 0;
256 #else /* HAVE_POLL */
257   fd_set readfds, writefds, errorfds;
258   struct timeval tv= {0,0};
259   int s_err;
260   SOCKET_SIZE_TYPE s_err_size= sizeof(s_err);
261 
262   FD_ZERO(&readfds);
263   FD_ZERO(&writefds);
264   FD_ZERO(&errorfds);
265 
266   my_FD_SET(sock, &readfds);
267   my_FD_SET(sock, &writefds);
268   my_FD_SET(sock, &errorfds);
269 
270   if(select(my_socket_nfds(sock,0)+1, &readfds, &writefds, &errorfds, &tv)<0)
271     return 1;
272 
273   if(my_FD_ISSET(sock,&errorfds))
274     return 1;
275 
276   s_err=0;
277   if (my_getsockopt(sock, SOL_SOCKET, SO_ERROR, &s_err, &s_err_size) != 0)
278     return(1);
279 
280   if (s_err)
281   {                                             /* getsockopt could succeed */
282     return(1);                                 /* but return an error... */
283   }
284 
285   return 0;
286 #endif /* HAVE_POLL */
287 }
288