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