1 | /*=========================================================================*\ |
2 | * Internet domain functions |
3 | * LuaSocket toolkit |
4 | \*=========================================================================*/ |
5 | #include <stdio.h> |
6 | #include <stdlib.h> |
7 | #include <string.h> |
8 | |
9 | #include "lua.h" |
10 | #include "lauxlib.h" |
11 | #include "compat.h" |
12 | |
13 | #include "inet.h" |
14 | |
15 | /*=========================================================================*\ |
16 | * Internal function prototypes. |
17 | \*=========================================================================*/ |
18 | static int inet_global_toip(lua_State *L); |
19 | static int inet_global_getaddrinfo(lua_State *L); |
20 | static int inet_global_tohostname(lua_State *L); |
21 | static int inet_global_getnameinfo(lua_State *L); |
22 | static void inet_pushresolved(lua_State *L, struct hostent *hp); |
23 | static int inet_global_gethostname(lua_State *L); |
24 | |
25 | /* DNS functions */ |
26 | static luaL_Reg func[] = { |
27 | { "toip" , inet_global_toip}, |
28 | { "getaddrinfo" , inet_global_getaddrinfo}, |
29 | { "tohostname" , inet_global_tohostname}, |
30 | { "getnameinfo" , inet_global_getnameinfo}, |
31 | { "gethostname" , inet_global_gethostname}, |
32 | { NULL, NULL} |
33 | }; |
34 | |
35 | /*=========================================================================*\ |
36 | * Exported functions |
37 | \*=========================================================================*/ |
38 | /*-------------------------------------------------------------------------*\ |
39 | * Initializes module |
40 | \*-------------------------------------------------------------------------*/ |
41 | int inet_open(lua_State *L) |
42 | { |
43 | lua_pushstring(L, "dns" ); |
44 | lua_newtable(L); |
45 | luaL_setfuncs(L, func, 0); |
46 | lua_settable(L, -3); |
47 | return 0; |
48 | } |
49 | |
50 | /*=========================================================================*\ |
51 | * Global Lua functions |
52 | \*=========================================================================*/ |
53 | /*-------------------------------------------------------------------------*\ |
54 | * Returns all information provided by the resolver given a host name |
55 | * or ip address |
56 | \*-------------------------------------------------------------------------*/ |
57 | static int inet_gethost(const char *address, struct hostent **hp) { |
58 | struct in_addr addr; |
59 | if (inet_aton(address, &addr)) |
60 | return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp); |
61 | else |
62 | return socket_gethostbyname(address, hp); |
63 | } |
64 | |
65 | /*-------------------------------------------------------------------------*\ |
66 | * Returns all information provided by the resolver given a host name |
67 | * or ip address |
68 | \*-------------------------------------------------------------------------*/ |
69 | static int inet_global_tohostname(lua_State *L) { |
70 | const char *address = luaL_checkstring(L, 1); |
71 | struct hostent *hp = NULL; |
72 | int err = inet_gethost(address, &hp); |
73 | if (err != IO_DONE) { |
74 | lua_pushnil(L); |
75 | lua_pushstring(L, socket_hoststrerror(err)); |
76 | return 2; |
77 | } |
78 | lua_pushstring(L, hp->h_name); |
79 | inet_pushresolved(L, hp); |
80 | return 2; |
81 | } |
82 | |
83 | static int inet_global_getnameinfo(lua_State *L) { |
84 | char hbuf[NI_MAXHOST]; |
85 | char sbuf[NI_MAXSERV]; |
86 | int i, ret; |
87 | struct addrinfo hints; |
88 | struct addrinfo *resolved, *iter; |
89 | const char *host = luaL_optstring(L, 1, NULL); |
90 | const char *serv = luaL_optstring(L, 2, NULL); |
91 | |
92 | if (!(host || serv)) |
93 | luaL_error(L, "host and serv cannot be both nil" ); |
94 | |
95 | memset(&hints, 0, sizeof(hints)); |
96 | hints.ai_socktype = SOCK_STREAM; |
97 | hints.ai_family = AF_UNSPEC; |
98 | |
99 | ret = getaddrinfo(host, serv, &hints, &resolved); |
100 | if (ret != 0) { |
101 | lua_pushnil(L); |
102 | lua_pushstring(L, socket_gaistrerror(ret)); |
103 | return 2; |
104 | } |
105 | |
106 | lua_newtable(L); |
107 | for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) { |
108 | getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen, |
109 | hbuf, host? (socklen_t) sizeof(hbuf): 0, |
110 | sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0); |
111 | if (host) { |
112 | lua_pushnumber(L, i); |
113 | lua_pushstring(L, hbuf); |
114 | lua_settable(L, -3); |
115 | } |
116 | } |
117 | freeaddrinfo(resolved); |
118 | |
119 | if (serv) { |
120 | lua_pushstring(L, sbuf); |
121 | return 2; |
122 | } else { |
123 | return 1; |
124 | } |
125 | } |
126 | |
127 | /*-------------------------------------------------------------------------*\ |
128 | * Returns all information provided by the resolver given a host name |
129 | * or ip address |
130 | \*-------------------------------------------------------------------------*/ |
131 | static int inet_global_toip(lua_State *L) |
132 | { |
133 | const char *address = luaL_checkstring(L, 1); |
134 | struct hostent *hp = NULL; |
135 | int err = inet_gethost(address, &hp); |
136 | if (err != IO_DONE) { |
137 | lua_pushnil(L); |
138 | lua_pushstring(L, socket_hoststrerror(err)); |
139 | return 2; |
140 | } |
141 | lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr))); |
142 | inet_pushresolved(L, hp); |
143 | return 2; |
144 | } |
145 | |
146 | int inet_optfamily(lua_State* L, int narg, const char* def) |
147 | { |
148 | static const char* optname[] = { "unspec" , "inet" , "inet6" , NULL }; |
149 | static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; |
150 | |
151 | return optvalue[luaL_checkoption(L, narg, def, optname)]; |
152 | } |
153 | |
154 | int inet_optsocktype(lua_State* L, int narg, const char* def) |
155 | { |
156 | static const char* optname[] = { "stream" , "dgram" , NULL }; |
157 | static int optvalue[] = { SOCK_STREAM, SOCK_DGRAM, 0 }; |
158 | |
159 | return optvalue[luaL_checkoption(L, narg, def, optname)]; |
160 | } |
161 | |
162 | static int inet_global_getaddrinfo(lua_State *L) |
163 | { |
164 | const char *hostname = luaL_checkstring(L, 1); |
165 | struct addrinfo *iterator = NULL, *resolved = NULL; |
166 | struct addrinfo hints; |
167 | int i = 1, ret = 0; |
168 | memset(&hints, 0, sizeof(hints)); |
169 | hints.ai_socktype = SOCK_STREAM; |
170 | hints.ai_family = AF_UNSPEC; |
171 | ret = getaddrinfo(hostname, NULL, &hints, &resolved); |
172 | if (ret != 0) { |
173 | lua_pushnil(L); |
174 | lua_pushstring(L, socket_gaistrerror(ret)); |
175 | return 2; |
176 | } |
177 | lua_newtable(L); |
178 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { |
179 | char hbuf[NI_MAXHOST]; |
180 | ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, |
181 | hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST); |
182 | if (ret){ |
183 | freeaddrinfo(resolved); |
184 | lua_pushnil(L); |
185 | lua_pushstring(L, socket_gaistrerror(ret)); |
186 | return 2; |
187 | } |
188 | lua_pushnumber(L, i); |
189 | lua_newtable(L); |
190 | switch (iterator->ai_family) { |
191 | case AF_INET: |
192 | lua_pushliteral(L, "family" ); |
193 | lua_pushliteral(L, "inet" ); |
194 | lua_settable(L, -3); |
195 | break; |
196 | case AF_INET6: |
197 | lua_pushliteral(L, "family" ); |
198 | lua_pushliteral(L, "inet6" ); |
199 | lua_settable(L, -3); |
200 | break; |
201 | case AF_UNSPEC: |
202 | lua_pushliteral(L, "family" ); |
203 | lua_pushliteral(L, "unspec" ); |
204 | lua_settable(L, -3); |
205 | break; |
206 | default: |
207 | lua_pushliteral(L, "family" ); |
208 | lua_pushliteral(L, "unknown" ); |
209 | lua_settable(L, -3); |
210 | break; |
211 | } |
212 | lua_pushliteral(L, "addr" ); |
213 | lua_pushstring(L, hbuf); |
214 | lua_settable(L, -3); |
215 | lua_settable(L, -3); |
216 | i++; |
217 | } |
218 | freeaddrinfo(resolved); |
219 | return 1; |
220 | } |
221 | |
222 | /*-------------------------------------------------------------------------*\ |
223 | * Gets the host name |
224 | \*-------------------------------------------------------------------------*/ |
225 | static int inet_global_gethostname(lua_State *L) |
226 | { |
227 | char name[257]; |
228 | name[256] = '\0'; |
229 | if (gethostname(name, 256) < 0) { |
230 | lua_pushnil(L); |
231 | lua_pushstring(L, socket_strerror(errno)); |
232 | return 2; |
233 | } else { |
234 | lua_pushstring(L, name); |
235 | return 1; |
236 | } |
237 | } |
238 | |
239 | /*=========================================================================*\ |
240 | * Lua methods |
241 | \*=========================================================================*/ |
242 | /*-------------------------------------------------------------------------*\ |
243 | * Retrieves socket peer name |
244 | \*-------------------------------------------------------------------------*/ |
245 | int inet_meth_getpeername(lua_State *L, p_socket ps, int family) |
246 | { |
247 | int err; |
248 | struct sockaddr_storage peer; |
249 | socklen_t peer_len = sizeof(peer); |
250 | char name[INET6_ADDRSTRLEN]; |
251 | char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ |
252 | if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { |
253 | lua_pushnil(L); |
254 | lua_pushstring(L, socket_strerror(errno)); |
255 | return 2; |
256 | } |
257 | err = getnameinfo((struct sockaddr *) &peer, peer_len, |
258 | name, INET6_ADDRSTRLEN, |
259 | port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); |
260 | if (err) { |
261 | lua_pushnil(L); |
262 | lua_pushstring(L, gai_strerror(err)); |
263 | return 2; |
264 | } |
265 | lua_pushstring(L, name); |
266 | lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); |
267 | switch (family) { |
268 | case AF_INET: lua_pushliteral(L, "inet" ); break; |
269 | case AF_INET6: lua_pushliteral(L, "inet6" ); break; |
270 | case AF_UNSPEC: lua_pushliteral(L, "unspec" ); break; |
271 | default: lua_pushliteral(L, "unknown" ); break; |
272 | } |
273 | return 3; |
274 | } |
275 | |
276 | /*-------------------------------------------------------------------------*\ |
277 | * Retrieves socket local name |
278 | \*-------------------------------------------------------------------------*/ |
279 | int inet_meth_getsockname(lua_State *L, p_socket ps, int family) |
280 | { |
281 | int err; |
282 | struct sockaddr_storage peer; |
283 | socklen_t peer_len = sizeof(peer); |
284 | char name[INET6_ADDRSTRLEN]; |
285 | char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ |
286 | if (getsockname(*ps, (SA *) &peer, &peer_len) < 0) { |
287 | lua_pushnil(L); |
288 | lua_pushstring(L, socket_strerror(errno)); |
289 | return 2; |
290 | } |
291 | err=getnameinfo((struct sockaddr *)&peer, peer_len, |
292 | name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); |
293 | if (err) { |
294 | lua_pushnil(L); |
295 | lua_pushstring(L, gai_strerror(err)); |
296 | return 2; |
297 | } |
298 | lua_pushstring(L, name); |
299 | lua_pushstring(L, port); |
300 | switch (family) { |
301 | case AF_INET: lua_pushliteral(L, "inet" ); break; |
302 | case AF_INET6: lua_pushliteral(L, "inet6" ); break; |
303 | case AF_UNSPEC: lua_pushliteral(L, "unspec" ); break; |
304 | default: lua_pushliteral(L, "unknown" ); break; |
305 | } |
306 | return 3; |
307 | } |
308 | |
309 | /*=========================================================================*\ |
310 | * Internal functions |
311 | \*=========================================================================*/ |
312 | /*-------------------------------------------------------------------------*\ |
313 | * Passes all resolver information to Lua as a table |
314 | \*-------------------------------------------------------------------------*/ |
315 | static void inet_pushresolved(lua_State *L, struct hostent *hp) |
316 | { |
317 | char **alias; |
318 | struct in_addr **addr; |
319 | int i, resolved; |
320 | lua_newtable(L); resolved = lua_gettop(L); |
321 | lua_pushstring(L, "name" ); |
322 | lua_pushstring(L, hp->h_name); |
323 | lua_settable(L, resolved); |
324 | lua_pushstring(L, "ip" ); |
325 | lua_pushstring(L, "alias" ); |
326 | i = 1; |
327 | alias = hp->h_aliases; |
328 | lua_newtable(L); |
329 | if (alias) { |
330 | while (*alias) { |
331 | lua_pushnumber(L, i); |
332 | lua_pushstring(L, *alias); |
333 | lua_settable(L, -3); |
334 | i++; alias++; |
335 | } |
336 | } |
337 | lua_settable(L, resolved); |
338 | i = 1; |
339 | lua_newtable(L); |
340 | addr = (struct in_addr **) hp->h_addr_list; |
341 | if (addr) { |
342 | while (*addr) { |
343 | lua_pushnumber(L, i); |
344 | lua_pushstring(L, inet_ntoa(**addr)); |
345 | lua_settable(L, -3); |
346 | i++; addr++; |
347 | } |
348 | } |
349 | lua_settable(L, resolved); |
350 | } |
351 | |
352 | /*-------------------------------------------------------------------------*\ |
353 | * Tries to create a new inet socket |
354 | \*-------------------------------------------------------------------------*/ |
355 | const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { |
356 | const char *err = socket_strerror(socket_create(ps, family, type, protocol)); |
357 | if (err == NULL && family == AF_INET6) { |
358 | int yes = 1; |
359 | setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); |
360 | } |
361 | return err; |
362 | } |
363 | |
364 | /*-------------------------------------------------------------------------*\ |
365 | * "Disconnects" a DGRAM socket |
366 | \*-------------------------------------------------------------------------*/ |
367 | const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) |
368 | { |
369 | switch (family) { |
370 | case AF_INET: { |
371 | struct sockaddr_in sin; |
372 | memset((char *) &sin, 0, sizeof(sin)); |
373 | sin.sin_family = AF_UNSPEC; |
374 | sin.sin_addr.s_addr = INADDR_ANY; |
375 | return socket_strerror(socket_connect(ps, (SA *) &sin, |
376 | sizeof(sin), tm)); |
377 | } |
378 | case AF_INET6: { |
379 | struct sockaddr_in6 sin6; |
380 | struct in6_addr addrany = IN6ADDR_ANY_INIT; |
381 | memset((char *) &sin6, 0, sizeof(sin6)); |
382 | sin6.sin6_family = AF_UNSPEC; |
383 | sin6.sin6_addr = addrany; |
384 | return socket_strerror(socket_connect(ps, (SA *) &sin6, |
385 | sizeof(sin6), tm)); |
386 | } |
387 | } |
388 | return NULL; |
389 | } |
390 | |
391 | /*-------------------------------------------------------------------------*\ |
392 | * Tries to connect to remote address (address, port) |
393 | \*-------------------------------------------------------------------------*/ |
394 | const char *inet_tryconnect(p_socket ps, int *family, const char *address, |
395 | const char *serv, p_timeout tm, struct addrinfo *connecthints) |
396 | { |
397 | struct addrinfo *iterator = NULL, *resolved = NULL; |
398 | const char *err = NULL; |
399 | int current_family = *family; |
400 | /* try resolving */ |
401 | err = socket_gaistrerror(getaddrinfo(address, serv, |
402 | connecthints, &resolved)); |
403 | if (err != NULL) { |
404 | if (resolved) freeaddrinfo(resolved); |
405 | return err; |
406 | } |
407 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { |
408 | timeout_markstart(tm); |
409 | /* create new socket if necessary. if there was no |
410 | * bind, we need to create one for every new family |
411 | * that shows up while iterating. if there was a |
412 | * bind, all families will be the same and we will |
413 | * not enter this branch. */ |
414 | if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { |
415 | socket_destroy(ps); |
416 | err = inet_trycreate(ps, iterator->ai_family, |
417 | iterator->ai_socktype, iterator->ai_protocol); |
418 | if (err) continue; |
419 | current_family = iterator->ai_family; |
420 | /* set non-blocking before connect */ |
421 | socket_setnonblocking(ps); |
422 | } |
423 | /* try connecting to remote address */ |
424 | err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, |
425 | (socklen_t) iterator->ai_addrlen, tm)); |
426 | /* if success or timeout is zero, break out of loop */ |
427 | if (err == NULL || timeout_iszero(tm)) { |
428 | *family = current_family; |
429 | break; |
430 | } |
431 | } |
432 | freeaddrinfo(resolved); |
433 | /* here, if err is set, we failed */ |
434 | return err; |
435 | } |
436 | |
437 | /*-------------------------------------------------------------------------*\ |
438 | * Tries to accept a socket |
439 | \*-------------------------------------------------------------------------*/ |
440 | const char *inet_tryaccept(p_socket server, int family, p_socket client, |
441 | p_timeout tm) { |
442 | socklen_t len; |
443 | t_sockaddr_storage addr; |
444 | switch (family) { |
445 | case AF_INET6: len = sizeof(struct sockaddr_in6); break; |
446 | case AF_INET: len = sizeof(struct sockaddr_in); break; |
447 | default: len = sizeof(addr); break; |
448 | } |
449 | return socket_strerror(socket_accept(server, client, (SA *) &addr, |
450 | &len, tm)); |
451 | } |
452 | |
453 | /*-------------------------------------------------------------------------*\ |
454 | * Tries to bind socket to (address, port) |
455 | \*-------------------------------------------------------------------------*/ |
456 | const char *inet_trybind(p_socket ps, int *family, const char *address, |
457 | const char *serv, struct addrinfo *bindhints) { |
458 | struct addrinfo *iterator = NULL, *resolved = NULL; |
459 | const char *err = NULL; |
460 | int current_family = *family; |
461 | /* translate luasocket special values to C */ |
462 | if (strcmp(address, "*" ) == 0) address = NULL; |
463 | if (!serv) serv = "0" ; |
464 | /* try resolving */ |
465 | err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved)); |
466 | if (err) { |
467 | if (resolved) freeaddrinfo(resolved); |
468 | return err; |
469 | } |
470 | /* iterate over resolved addresses until one is good */ |
471 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { |
472 | if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { |
473 | socket_destroy(ps); |
474 | err = inet_trycreate(ps, iterator->ai_family, |
475 | iterator->ai_socktype, iterator->ai_protocol); |
476 | if (err) continue; |
477 | current_family = iterator->ai_family; |
478 | } |
479 | /* try binding to local address */ |
480 | err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr, |
481 | (socklen_t) iterator->ai_addrlen)); |
482 | /* keep trying unless bind succeeded */ |
483 | if (err == NULL) { |
484 | *family = current_family; |
485 | /* set to non-blocking after bind */ |
486 | socket_setnonblocking(ps); |
487 | break; |
488 | } |
489 | } |
490 | /* cleanup and return error */ |
491 | freeaddrinfo(resolved); |
492 | /* here, if err is set, we failed */ |
493 | return err; |
494 | } |
495 | |
496 | /*-------------------------------------------------------------------------*\ |
497 | * Some systems do not provide these so that we provide our own. |
498 | \*-------------------------------------------------------------------------*/ |
499 | #ifdef LUASOCKET_INET_ATON |
500 | int inet_aton(const char *cp, struct in_addr *inp) |
501 | { |
502 | unsigned int a = 0, b = 0, c = 0, d = 0; |
503 | int n = 0, r; |
504 | unsigned long int addr = 0; |
505 | r = sscanf(cp, "%u.%u.%u.%u%n" , &a, &b, &c, &d, &n); |
506 | if (r == 0 || n == 0) return 0; |
507 | cp += n; |
508 | if (*cp) return 0; |
509 | if (a > 255 || b > 255 || c > 255 || d > 255) return 0; |
510 | if (inp) { |
511 | addr += a; addr <<= 8; |
512 | addr += b; addr <<= 8; |
513 | addr += c; addr <<= 8; |
514 | addr += d; |
515 | inp->s_addr = htonl(addr); |
516 | } |
517 | return 1; |
518 | } |
519 | #endif |
520 | |
521 | #ifdef LUASOCKET_INET_PTON |
522 | int inet_pton(int af, const char *src, void *dst) |
523 | { |
524 | struct addrinfo hints, *res; |
525 | int ret = 1; |
526 | memset(&hints, 0, sizeof(struct addrinfo)); |
527 | hints.ai_family = af; |
528 | hints.ai_flags = AI_NUMERICHOST; |
529 | if (getaddrinfo(src, NULL, &hints, &res) != 0) return -1; |
530 | if (af == AF_INET) { |
531 | struct sockaddr_in *in = (struct sockaddr_in *) res->ai_addr; |
532 | memcpy(dst, &in->sin_addr, sizeof(in->sin_addr)); |
533 | } else if (af == AF_INET6) { |
534 | struct sockaddr_in6 *in = (struct sockaddr_in6 *) res->ai_addr; |
535 | memcpy(dst, &in->sin6_addr, sizeof(in->sin6_addr)); |
536 | } else { |
537 | ret = -1; |
538 | } |
539 | freeaddrinfo(res); |
540 | return ret; |
541 | } |
542 | |
543 | #endif |
544 | |