4 |
* |
* |
5 |
* Connect to host at port using address resolution from getaddrinfo |
* 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 <sys/socket.h> /* netinet/in.h needs it */ |
11 |
#include <netinet/in.h> |
#include <netinet/in.h> |
12 |
#include <net/if.h> |
#include <net/if.h> |
13 |
|
#include <sys/un.h> |
14 |
#include "libbb.h" |
#include "libbb.h" |
15 |
|
|
16 |
void FAST_FUNC setsockopt_reuseaddr(int fd) |
void FAST_FUNC setsockopt_reuseaddr(int fd) |
25 |
{ |
{ |
26 |
int r; |
int r; |
27 |
struct ifreq ifr; |
struct ifreq ifr; |
28 |
strncpy(ifr.ifr_name, iface, IFNAMSIZ); |
strncpy_IFNAMSIZ(ifr.ifr_name, iface); |
29 |
/* Actually, ifr_name is at offset 0, and in practice |
/* NB: passing (iface, strlen(iface) + 1) does not work! |
30 |
|
* (maybe it works on _some_ kernels, but not on 2.6.26) |
31 |
|
* Actually, ifr_name is at offset 0, and in practice |
32 |
* just giving char[IFNAMSIZ] instead of struct ifreq works too. |
* just giving char[IFNAMSIZ] instead of struct ifreq works too. |
33 |
* But just in case it's not true on some obscure arch... */ |
* But just in case it's not true on some obscure arch... */ |
34 |
r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); |
r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); |
37 |
return r; |
return r; |
38 |
} |
} |
39 |
|
|
40 |
|
len_and_sockaddr* FAST_FUNC get_sock_lsa(int fd) |
41 |
|
{ |
42 |
|
len_and_sockaddr lsa; |
43 |
|
len_and_sockaddr *lsa_ptr; |
44 |
|
|
45 |
|
lsa.len = LSA_SIZEOF_SA; |
46 |
|
if (getsockname(fd, &lsa.u.sa, &lsa.len) != 0) |
47 |
|
return NULL; |
48 |
|
|
49 |
|
lsa_ptr = xzalloc(LSA_LEN_SIZE + lsa.len); |
50 |
|
if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */ |
51 |
|
lsa_ptr->len = lsa.len; |
52 |
|
getsockname(fd, &lsa_ptr->u.sa, &lsa_ptr->len); |
53 |
|
} else { |
54 |
|
memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len); |
55 |
|
} |
56 |
|
return lsa_ptr; |
57 |
|
} |
58 |
|
|
59 |
void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) |
void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) |
60 |
{ |
{ |
63 |
close(s); |
close(s); |
64 |
if (s_addr->sa_family == AF_INET) |
if (s_addr->sa_family == AF_INET) |
65 |
bb_perror_msg_and_die("%s (%s)", |
bb_perror_msg_and_die("%s (%s)", |
66 |
"cannot connect to remote host", |
"can't connect to remote host", |
67 |
inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); |
inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); |
68 |
bb_perror_msg_and_die("cannot connect to remote host"); |
bb_perror_msg_and_die("can't connect to remote host"); |
69 |
} |
} |
70 |
} |
} |
71 |
|
|
72 |
/* Return port number for a service. |
/* Return port number for a service. |
73 |
* If "port" is a number use it as the port. |
* If "port" is a number use it as the port. |
74 |
* If "port" is a name it is looked up in /etc/services, if it isnt found return |
* If "port" is a name it is looked up in /etc/services, |
75 |
* default_port */ |
* if it isnt found return default_port |
76 |
|
*/ |
77 |
unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port) |
unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port) |
78 |
{ |
{ |
79 |
unsigned port_nr = default_port; |
unsigned port_nr = default_port; |
158 |
* port: if neither of above specifies port # */ |
* port: if neither of above specifies port # */ |
159 |
static len_and_sockaddr* str2sockaddr( |
static len_and_sockaddr* str2sockaddr( |
160 |
const char *host, int port, |
const char *host, int port, |
161 |
USE_FEATURE_IPV6(sa_family_t af,) |
IF_FEATURE_IPV6(sa_family_t af,) |
162 |
int ai_flags) |
int ai_flags) |
163 |
{ |
{ |
164 |
|
IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;) |
165 |
int rc; |
int rc; |
166 |
len_and_sockaddr *r = NULL; |
len_and_sockaddr *r; |
167 |
struct addrinfo *result = NULL; |
struct addrinfo *result = NULL; |
168 |
struct addrinfo *used_res; |
struct addrinfo *used_res; |
169 |
const char *org_host = host; /* only for error msg */ |
const char *org_host = host; /* only for error msg */ |
170 |
const char *cp; |
const char *cp; |
171 |
struct addrinfo hint; |
struct addrinfo hint; |
172 |
|
|
173 |
|
if (ENABLE_FEATURE_UNIX_LOCAL && strncmp(host, "local:", 6) == 0) { |
174 |
|
struct sockaddr_un *sun; |
175 |
|
|
176 |
|
r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un)); |
177 |
|
r->len = sizeof(struct sockaddr_un); |
178 |
|
r->u.sa.sa_family = AF_UNIX; |
179 |
|
sun = (struct sockaddr_un *)&r->u.sa; |
180 |
|
safe_strncpy(sun->sun_path, host + 6, sizeof(sun->sun_path)); |
181 |
|
return r; |
182 |
|
} |
183 |
|
|
184 |
|
r = NULL; |
185 |
|
|
186 |
/* Ugly parsing of host:addr */ |
/* Ugly parsing of host:addr */ |
187 |
if (ENABLE_FEATURE_IPV6 && host[0] == '[') { |
if (ENABLE_FEATURE_IPV6 && host[0] == '[') { |
188 |
/* Even uglier parsing of [xx]:nn */ |
/* Even uglier parsing of [xx]:nn */ |
189 |
host++; |
host++; |
190 |
cp = strchr(host, ']'); |
cp = strchr(host, ']'); |
191 |
if (!cp || cp[1] != ':') { /* Malformed: must have [xx]:nn */ |
if (!cp || (cp[1] != ':' && cp[1] != '\0')) { |
192 |
|
/* Malformed: must be [xx]:nn or [xx] */ |
193 |
bb_error_msg("bad address '%s'", org_host); |
bb_error_msg("bad address '%s'", org_host); |
194 |
if (ai_flags & DIE_ON_ERROR) |
if (ai_flags & DIE_ON_ERROR) |
195 |
xfunc_die(); |
xfunc_die(); |
204 |
} |
} |
205 |
if (cp) { /* points to ":" or "]:" */ |
if (cp) { /* points to ":" or "]:" */ |
206 |
int sz = cp - host + 1; |
int sz = cp - host + 1; |
207 |
|
|
208 |
host = safe_strncpy(alloca(sz), host, sz); |
host = safe_strncpy(alloca(sz), host, sz); |
209 |
if (ENABLE_FEATURE_IPV6 && *cp != ':') |
if (ENABLE_FEATURE_IPV6 && *cp != ':') { |
210 |
cp++; /* skip ']' */ |
cp++; /* skip ']' */ |
211 |
|
if (*cp == '\0') /* [xx] without port */ |
212 |
|
goto skip; |
213 |
|
} |
214 |
cp++; /* skip ':' */ |
cp++; /* skip ':' */ |
215 |
port = bb_strtou(cp, NULL, 10); |
port = bb_strtou(cp, NULL, 10); |
216 |
if (errno || (unsigned)port > 0xffff) { |
if (errno || (unsigned)port > 0xffff) { |
219 |
xfunc_die(); |
xfunc_die(); |
220 |
return NULL; |
return NULL; |
221 |
} |
} |
222 |
|
skip: ; |
223 |
} |
} |
224 |
|
|
225 |
|
/* Next two if blocks allow to skip getaddrinfo() |
226 |
|
* in case host name is a numeric IP(v6) address. |
227 |
|
* getaddrinfo() initializes DNS resolution machinery, |
228 |
|
* scans network config and such - tens of syscalls. |
229 |
|
*/ |
230 |
|
/* If we were not asked specifically for IPv6, |
231 |
|
* check whether this is a numeric IPv4 */ |
232 |
|
IF_FEATURE_IPV6(if(af != AF_INET6)) { |
233 |
|
struct in_addr in4; |
234 |
|
if (inet_aton(host, &in4) != 0) { |
235 |
|
r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in)); |
236 |
|
r->len = sizeof(struct sockaddr_in); |
237 |
|
r->u.sa.sa_family = AF_INET; |
238 |
|
r->u.sin.sin_addr = in4; |
239 |
|
goto set_port; |
240 |
|
} |
241 |
|
} |
242 |
|
#if ENABLE_FEATURE_IPV6 |
243 |
|
/* If we were not asked specifically for IPv4, |
244 |
|
* check whether this is a numeric IPv6 */ |
245 |
|
if (af != AF_INET) { |
246 |
|
struct in6_addr in6; |
247 |
|
if (inet_pton(AF_INET6, host, &in6) > 0) { |
248 |
|
r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in6)); |
249 |
|
r->len = sizeof(struct sockaddr_in6); |
250 |
|
r->u.sa.sa_family = AF_INET6; |
251 |
|
r->u.sin6.sin6_addr = in6; |
252 |
|
goto set_port; |
253 |
|
} |
254 |
|
} |
255 |
|
#endif |
256 |
|
|
257 |
memset(&hint, 0 , sizeof(hint)); |
memset(&hint, 0 , sizeof(hint)); |
|
#if !ENABLE_FEATURE_IPV6 |
|
|
hint.ai_family = AF_INET; /* do not try to find IPv6 */ |
|
|
#else |
|
258 |
hint.ai_family = af; |
hint.ai_family = af; |
|
#endif |
|
259 |
/* Needed. Or else we will get each address thrice (or more) |
/* Needed. Or else we will get each address thrice (or more) |
260 |
* for each possible socket type (tcp,udp,raw...): */ |
* for each possible socket type (tcp,udp,raw...): */ |
261 |
hint.ai_socktype = SOCK_STREAM; |
hint.ai_socktype = SOCK_STREAM; |
279 |
} |
} |
280 |
} |
} |
281 |
#endif |
#endif |
282 |
r = xmalloc(offsetof(len_and_sockaddr, u.sa) + used_res->ai_addrlen); |
r = xmalloc(LSA_LEN_SIZE + used_res->ai_addrlen); |
283 |
r->len = used_res->ai_addrlen; |
r->len = used_res->ai_addrlen; |
284 |
memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen); |
memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen); |
285 |
|
|
286 |
|
set_port: |
287 |
set_nport(r, htons(port)); |
set_nport(r, htons(port)); |
288 |
ret: |
ret: |
289 |
freeaddrinfo(result); |
freeaddrinfo(result); |
321 |
} |
} |
322 |
|
|
323 |
#undef xsocket_type |
#undef xsocket_type |
324 |
int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, USE_FEATURE_IPV6(int family,) int sock_type) |
int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, IF_FEATURE_IPV6(int family,) int sock_type) |
325 |
{ |
{ |
326 |
SKIP_FEATURE_IPV6(enum { family = AF_INET };) |
IF_NOT_FEATURE_IPV6(enum { family = AF_INET };) |
327 |
len_and_sockaddr *lsa; |
len_and_sockaddr *lsa; |
328 |
int fd; |
int fd; |
329 |
int len; |
int len; |
346 |
len = sizeof(struct sockaddr_in6); |
len = sizeof(struct sockaddr_in6); |
347 |
} |
} |
348 |
#endif |
#endif |
349 |
lsa = xzalloc(offsetof(len_and_sockaddr, u.sa) + len); |
lsa = xzalloc(LSA_LEN_SIZE + len); |
350 |
lsa->len = len; |
lsa->len = len; |
351 |
lsa->u.sa.sa_family = family; |
lsa->u.sa.sa_family = family; |
352 |
*lsap = lsa; |
*lsap = lsa; |
355 |
|
|
356 |
int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap) |
int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap) |
357 |
{ |
{ |
358 |
return xsocket_type(lsap, USE_FEATURE_IPV6(AF_UNSPEC,) SOCK_STREAM); |
return xsocket_type(lsap, IF_FEATURE_IPV6(AF_UNSPEC,) SOCK_STREAM); |
359 |
} |
} |
360 |
|
|
361 |
static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) |
static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) |
368 |
/* user specified bind addr dictates family */ |
/* user specified bind addr dictates family */ |
369 |
fd = xsocket(lsa->u.sa.sa_family, sock_type, 0); |
fd = xsocket(lsa->u.sa.sa_family, sock_type, 0); |
370 |
} else { |
} else { |
371 |
fd = xsocket_type(&lsa, USE_FEATURE_IPV6(AF_UNSPEC,) sock_type); |
fd = xsocket_type(&lsa, IF_FEATURE_IPV6(AF_UNSPEC,) sock_type); |
372 |
set_nport(lsa, htons(port)); |
set_nport(lsa, htons(port)); |
373 |
} |
} |
374 |
setsockopt_reuseaddr(fd); |
setsockopt_reuseaddr(fd); |
418 |
int rc; |
int rc; |
419 |
socklen_t salen; |
socklen_t salen; |
420 |
|
|
421 |
|
if (ENABLE_FEATURE_UNIX_LOCAL && sa->sa_family == AF_UNIX) { |
422 |
|
struct sockaddr_un *sun = (struct sockaddr_un *)sa; |
423 |
|
return xasprintf("local:%.*s", |
424 |
|
(int) sizeof(sun->sun_path), |
425 |
|
sun->sun_path); |
426 |
|
} |
427 |
|
|
428 |
salen = LSA_SIZEOF_SA; |
salen = LSA_SIZEOF_SA; |
429 |
#if ENABLE_FEATURE_IPV6 |
#if ENABLE_FEATURE_IPV6 |
430 |
if (sa->sa_family == AF_INET) |
if (sa->sa_family == AF_INET) |