source: MondoRescue/branches/3.3/mindi-busybox/libbb/xconnect.c@ 3647

Last change on this file since 3647 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 12.9 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Connect to host at port using address resolution from getaddrinfo
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9
10#include <sys/types.h>
11#include <sys/socket.h> /* netinet/in.h needs it */
12#include <netinet/in.h>
13#include <net/if.h>
14#include <sys/un.h>
15#include "libbb.h"
16
17int FAST_FUNC setsockopt_int(int fd, int level, int optname, int optval)
18{
19 return setsockopt(fd, level, optname, &optval, sizeof(int));
20}
21int FAST_FUNC setsockopt_1(int fd, int level, int optname)
22{
23 return setsockopt_int(fd, level, optname, 1);
24}
25int FAST_FUNC setsockopt_SOL_SOCKET_int(int fd, int optname, int optval)
26{
27 return setsockopt_int(fd, SOL_SOCKET, optname, optval);
28}
29int FAST_FUNC setsockopt_SOL_SOCKET_1(int fd, int optname)
30{
31 return setsockopt_SOL_SOCKET_int(fd, optname, 1);
32}
33
34void FAST_FUNC setsockopt_reuseaddr(int fd)
35{
36 setsockopt_SOL_SOCKET_1(fd, SO_REUSEADDR);
37}
38int FAST_FUNC setsockopt_broadcast(int fd)
39{
40 return setsockopt_SOL_SOCKET_1(fd, SO_BROADCAST);
41}
42int FAST_FUNC setsockopt_keepalive(int fd)
43{
44 return setsockopt_SOL_SOCKET_1(fd, SO_KEEPALIVE);
45}
46
47#ifdef SO_BINDTODEVICE
48int FAST_FUNC setsockopt_bindtodevice(int fd, const char *iface)
49{
50 int r;
51 struct ifreq ifr;
52 strncpy_IFNAMSIZ(ifr.ifr_name, iface);
53 /* NB: passing (iface, strlen(iface) + 1) does not work!
54 * (maybe it works on _some_ kernels, but not on 2.6.26)
55 * Actually, ifr_name is at offset 0, and in practice
56 * just giving char[IFNAMSIZ] instead of struct ifreq works too.
57 * But just in case it's not true on some obscure arch... */
58 r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
59 if (r)
60 bb_perror_msg("can't bind to interface %s", iface);
61 return r;
62}
63#else
64int FAST_FUNC setsockopt_bindtodevice(int fd UNUSED_PARAM,
65 const char *iface UNUSED_PARAM)
66{
67 bb_error_msg("SO_BINDTODEVICE is not supported on this system");
68 return -1;
69}
70#endif
71
72static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen))
73{
74 len_and_sockaddr lsa;
75 len_and_sockaddr *lsa_ptr;
76
77 lsa.len = LSA_SIZEOF_SA;
78 if (get_name(fd, &lsa.u.sa, &lsa.len) != 0)
79 return NULL;
80
81 lsa_ptr = xzalloc(LSA_LEN_SIZE + lsa.len);
82 if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */
83 lsa_ptr->len = lsa.len;
84 get_name(fd, &lsa_ptr->u.sa, &lsa_ptr->len);
85 } else {
86 memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len);
87 }
88 return lsa_ptr;
89}
90
91len_and_sockaddr* FAST_FUNC get_sock_lsa(int fd)
92{
93 return get_lsa(fd, getsockname);
94}
95
96len_and_sockaddr* FAST_FUNC get_peer_lsa(int fd)
97{
98 return get_lsa(fd, getpeername);
99}
100
101void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
102{
103 if (connect(s, s_addr, addrlen) < 0) {
104 if (ENABLE_FEATURE_CLEAN_UP)
105 close(s);
106 if (s_addr->sa_family == AF_INET)
107 bb_perror_msg_and_die("%s (%s)",
108 "can't connect to remote host",
109 inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr));
110 bb_perror_msg_and_die("can't connect to remote host");
111 }
112}
113
114/* Return port number for a service.
115 * If "port" is a number use it as the port.
116 * If "port" is a name it is looked up in /etc/services,
117 * if it isnt found return default_port
118 */
119unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port)
120{
121 unsigned port_nr = default_port;
122 if (port) {
123 int old_errno;
124
125 /* Since this is a lib function, we're not allowed to reset errno to 0.
126 * Doing so could break an app that is deferring checking of errno. */
127 old_errno = errno;
128 port_nr = bb_strtou(port, NULL, 10);
129 if (errno || port_nr > 65535) {
130 struct servent *tserv = getservbyname(port, protocol);
131 port_nr = default_port;
132 if (tserv)
133 port_nr = ntohs(tserv->s_port);
134 }
135 errno = old_errno;
136 }
137 return (uint16_t)port_nr;
138}
139
140
141/* "New" networking API */
142
143
144int FAST_FUNC get_nport(const struct sockaddr *sa)
145{
146#if ENABLE_FEATURE_IPV6
147 if (sa->sa_family == AF_INET6) {
148 return ((struct sockaddr_in6*)sa)->sin6_port;
149 }
150#endif
151 if (sa->sa_family == AF_INET) {
152 return ((struct sockaddr_in*)sa)->sin_port;
153 }
154 /* What? UNIX socket? IPX?? :) */
155 return -1;
156}
157
158void FAST_FUNC set_nport(struct sockaddr *sa, unsigned port)
159{
160#if ENABLE_FEATURE_IPV6
161 if (sa->sa_family == AF_INET6) {
162 struct sockaddr_in6 *sin6 = (void*) sa;
163 sin6->sin6_port = port;
164 return;
165 }
166#endif
167 if (sa->sa_family == AF_INET) {
168 struct sockaddr_in *sin = (void*) sa;
169 sin->sin_port = port;
170 return;
171 }
172 /* What? UNIX socket? IPX?? :) */
173}
174
175/* We hijack this constant to mean something else */
176/* It doesn't hurt because we will remove this bit anyway */
177#define DIE_ON_ERROR AI_CANONNAME
178
179/* host: "1.2.3.4[:port]", "www.google.com[:port]"
180 * port: if neither of above specifies port # */
181static len_and_sockaddr* str2sockaddr(
182 const char *host, int port,
183IF_FEATURE_IPV6(sa_family_t af,)
184 int ai_flags)
185{
186IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;)
187 int rc;
188 len_and_sockaddr *r;
189 struct addrinfo *result = NULL;
190 struct addrinfo *used_res;
191 const char *org_host = host; /* only for error msg */
192 const char *cp;
193 struct addrinfo hint;
194
195 if (ENABLE_FEATURE_UNIX_LOCAL && is_prefixed_with(host, "local:")) {
196 struct sockaddr_un *sun;
197
198 r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un));
199 r->len = sizeof(struct sockaddr_un);
200 r->u.sa.sa_family = AF_UNIX;
201 sun = (struct sockaddr_un *)&r->u.sa;
202 safe_strncpy(sun->sun_path, host + 6, sizeof(sun->sun_path));
203 return r;
204 }
205
206 r = NULL;
207
208 /* Ugly parsing of host:addr */
209 if (ENABLE_FEATURE_IPV6 && host[0] == '[') {
210 /* Even uglier parsing of [xx]:nn */
211 host++;
212 cp = strchr(host, ']');
213 if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
214 /* Malformed: must be [xx]:nn or [xx] */
215 bb_error_msg("bad address '%s'", org_host);
216 if (ai_flags & DIE_ON_ERROR)
217 xfunc_die();
218 return NULL;
219 }
220 } else {
221 cp = strrchr(host, ':');
222 if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) {
223 /* There is more than one ':' (e.g. "::1") */
224 cp = NULL; /* it's not a port spec */
225 }
226 }
227 if (cp) { /* points to ":" or "]:" */
228 int sz = cp - host + 1;
229
230 host = safe_strncpy(alloca(sz), host, sz);
231 if (ENABLE_FEATURE_IPV6 && *cp != ':') {
232 cp++; /* skip ']' */
233 if (*cp == '\0') /* [xx] without port */
234 goto skip;
235 }
236 cp++; /* skip ':' */
237 port = bb_strtou(cp, NULL, 10);
238 if (errno || (unsigned)port > 0xffff) {
239 bb_error_msg("bad port spec '%s'", org_host);
240 if (ai_flags & DIE_ON_ERROR)
241 xfunc_die();
242 return NULL;
243 }
244 skip: ;
245 }
246
247 /* Next two if blocks allow to skip getaddrinfo()
248 * in case host name is a numeric IP(v6) address.
249 * getaddrinfo() initializes DNS resolution machinery,
250 * scans network config and such - tens of syscalls.
251 */
252 /* If we were not asked specifically for IPv6,
253 * check whether this is a numeric IPv4 */
254 IF_FEATURE_IPV6(if(af != AF_INET6)) {
255 struct in_addr in4;
256 if (inet_aton(host, &in4) != 0) {
257 r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in));
258 r->len = sizeof(struct sockaddr_in);
259 r->u.sa.sa_family = AF_INET;
260 r->u.sin.sin_addr = in4;
261 goto set_port;
262 }
263 }
264#if ENABLE_FEATURE_IPV6
265 /* If we were not asked specifically for IPv4,
266 * check whether this is a numeric IPv6 */
267 if (af != AF_INET) {
268 struct in6_addr in6;
269 if (inet_pton(AF_INET6, host, &in6) > 0) {
270 r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in6));
271 r->len = sizeof(struct sockaddr_in6);
272 r->u.sa.sa_family = AF_INET6;
273 r->u.sin6.sin6_addr = in6;
274 goto set_port;
275 }
276 }
277#endif
278
279 memset(&hint, 0 , sizeof(hint));
280 hint.ai_family = af;
281 /* Need SOCK_STREAM, or else we get each address thrice (or more)
282 * for each possible socket type (tcp,udp,raw...): */
283 hint.ai_socktype = SOCK_STREAM;
284 hint.ai_flags = ai_flags & ~DIE_ON_ERROR;
285 rc = getaddrinfo(host, NULL, &hint, &result);
286 if (rc || !result) {
287 bb_error_msg("bad address '%s'", org_host);
288 if (ai_flags & DIE_ON_ERROR)
289 xfunc_die();
290 goto ret;
291 }
292 used_res = result;
293#if ENABLE_FEATURE_PREFER_IPV4_ADDRESS
294 while (1) {
295 if (used_res->ai_family == AF_INET)
296 break;
297 used_res = used_res->ai_next;
298 if (!used_res) {
299 used_res = result;
300 break;
301 }
302 }
303#endif
304 r = xmalloc(LSA_LEN_SIZE + used_res->ai_addrlen);
305 r->len = used_res->ai_addrlen;
306 memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen);
307
308 set_port:
309 set_nport(&r->u.sa, htons(port));
310 ret:
311 if (result)
312 freeaddrinfo(result);
313 return r;
314}
315#if !ENABLE_FEATURE_IPV6
316#define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags)
317#endif
318
319#if ENABLE_FEATURE_IPV6
320len_and_sockaddr* FAST_FUNC host_and_af2sockaddr(const char *host, int port, sa_family_t af)
321{
322 return str2sockaddr(host, port, af, 0);
323}
324
325len_and_sockaddr* FAST_FUNC xhost_and_af2sockaddr(const char *host, int port, sa_family_t af)
326{
327 return str2sockaddr(host, port, af, DIE_ON_ERROR);
328}
329#endif
330
331len_and_sockaddr* FAST_FUNC host2sockaddr(const char *host, int port)
332{
333 return str2sockaddr(host, port, AF_UNSPEC, 0);
334}
335
336len_and_sockaddr* FAST_FUNC xhost2sockaddr(const char *host, int port)
337{
338 return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR);
339}
340
341len_and_sockaddr* FAST_FUNC xdotted2sockaddr(const char *host, int port)
342{
343 return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR);
344}
345
346int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, int family, int sock_type)
347{
348 len_and_sockaddr *lsa;
349 int fd;
350 int len;
351
352 if (family == AF_UNSPEC) {
353#if ENABLE_FEATURE_IPV6
354 fd = socket(AF_INET6, sock_type, 0);
355 if (fd >= 0) {
356 family = AF_INET6;
357 goto done;
358 }
359#endif
360 family = AF_INET;
361 }
362
363 fd = xsocket(family, sock_type, 0);
364
365 len = sizeof(struct sockaddr_in);
366 if (family == AF_UNIX)
367 len = sizeof(struct sockaddr_un);
368#if ENABLE_FEATURE_IPV6
369 if (family == AF_INET6) {
370 done:
371 len = sizeof(struct sockaddr_in6);
372 }
373#endif
374 lsa = xzalloc(LSA_LEN_SIZE + len);
375 lsa->len = len;
376 lsa->u.sa.sa_family = family;
377 *lsap = lsa;
378 return fd;
379}
380
381int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap)
382{
383 return xsocket_type(lsap, AF_UNSPEC, SOCK_STREAM);
384}
385
386static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type)
387{
388 int fd;
389 len_and_sockaddr *lsa;
390
391 if (bindaddr && bindaddr[0]) {
392 lsa = xdotted2sockaddr(bindaddr, port);
393 /* user specified bind addr dictates family */
394 fd = xsocket(lsa->u.sa.sa_family, sock_type, 0);
395 } else {
396 fd = xsocket_type(&lsa, AF_UNSPEC, sock_type);
397 set_nport(&lsa->u.sa, htons(port));
398 }
399 setsockopt_reuseaddr(fd);
400 xbind(fd, &lsa->u.sa, lsa->len);
401 free(lsa);
402 return fd;
403}
404
405int FAST_FUNC create_and_bind_stream_or_die(const char *bindaddr, int port)
406{
407 return create_and_bind_or_die(bindaddr, port, SOCK_STREAM);
408}
409
410int FAST_FUNC create_and_bind_dgram_or_die(const char *bindaddr, int port)
411{
412 return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM);
413}
414
415
416int FAST_FUNC create_and_connect_stream_or_die(const char *peer, int port)
417{
418 int fd;
419 len_and_sockaddr *lsa;
420
421 lsa = xhost2sockaddr(peer, port);
422 fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
423 setsockopt_reuseaddr(fd);
424 xconnect(fd, &lsa->u.sa, lsa->len);
425 free(lsa);
426 return fd;
427}
428
429int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa)
430{
431 int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
432 xconnect(fd, &lsa->u.sa, lsa->len);
433 return fd;
434}
435
436/* We hijack this constant to mean something else */
437/* It doesn't hurt because we will add this bit anyway */
438#define IGNORE_PORT NI_NUMERICSERV
439static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags)
440{
441 char host[128];
442 char serv[16];
443 int rc;
444 socklen_t salen;
445
446 if (ENABLE_FEATURE_UNIX_LOCAL && sa->sa_family == AF_UNIX) {
447 struct sockaddr_un *sun = (struct sockaddr_un *)sa;
448 return xasprintf("local:%.*s",
449 (int) sizeof(sun->sun_path),
450 sun->sun_path);
451 }
452
453 salen = LSA_SIZEOF_SA;
454#if ENABLE_FEATURE_IPV6
455 if (sa->sa_family == AF_INET)
456 salen = sizeof(struct sockaddr_in);
457 if (sa->sa_family == AF_INET6)
458 salen = sizeof(struct sockaddr_in6);
459#endif
460 rc = getnameinfo(sa, salen,
461 host, sizeof(host),
462 /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */
463 serv, sizeof(serv),
464 /* do not resolve port# into service _name_ */
465 flags | NI_NUMERICSERV
466 );
467 if (rc)
468 return NULL;
469 if (flags & IGNORE_PORT)
470 return xstrdup(host);
471#if ENABLE_FEATURE_IPV6
472 if (sa->sa_family == AF_INET6) {
473 if (strchr(host, ':')) /* heh, it's not a resolved hostname */
474 return xasprintf("[%s]:%s", host, serv);
475 /*return xasprintf("%s:%s", host, serv);*/
476 /* - fall through instead */
477 }
478#endif
479 /* For now we don't support anything else, so it has to be INET */
480 /*if (sa->sa_family == AF_INET)*/
481 return xasprintf("%s:%s", host, serv);
482 /*return xstrdup(host);*/
483}
484
485char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa)
486{
487 return sockaddr2str(sa, 0);
488}
489
490char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa)
491{
492 return sockaddr2str(sa, IGNORE_PORT);
493}
494
495char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa)
496{
497 return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT);
498}
499char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa)
500{
501 return sockaddr2str(sa, NI_NUMERICHOST);
502}
503
504char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa)
505{
506 return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT);
507}
Note: See TracBrowser for help on using the repository browser.