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