aboutsummaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
authorvda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2007-01-10 09:28:01 +0000
committervda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2007-01-10 09:28:01 +0000
commit5dfdd30e48c5b7e5d1a1fbdb569a1fed98e4ae3a (patch)
tree01c0783874d5c429b4f71496f18028d46afae302 /libbb
parent0b31397d0da6d009b53bb22f359bc018d1825bfa (diff)
downloadbusybox-w32-5dfdd30e48c5b7e5d1a1fbdb569a1fed98e4ae3a.tar.gz
busybox-w32-5dfdd30e48c5b7e5d1a1fbdb569a1fed98e4ae3a.tar.bz2
busybox-w32-5dfdd30e48c5b7e5d1a1fbdb569a1fed98e4ae3a.zip
Improve generic ipv4+ipv6 support in libbb.
Convert telnet to it. Now this works: telnetd -b [::1]:1234 - bind to IPv6 non-standard port telnet [::1]:1234 - connect to IPv6 non-standard port telnet ::1 1234 - same This does not require ANY ipv6-specific code in applets (no struct sockaddr_in6. In fact, no sockaddr_in, too). git-svn-id: svn://busybox.net/trunk/busybox@17221 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to 'libbb')
-rw-r--r--libbb/xconnect.c234
1 files changed, 151 insertions, 83 deletions
diff --git a/libbb/xconnect.c b/libbb/xconnect.c
index bc0691531..93c3cd5c6 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -6,39 +6,62 @@
6 * 6 *
7 */ 7 */
8 8
9#include <netinet/in.h>
9#include "libbb.h" 10#include "libbb.h"
10 11
12static const int one = 1;
13int setsockopt_reuseaddr(int fd)
14{
15 return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
16}
17int setsockopt_broadcast(int fd)
18{
19 return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one));
20}
21
22void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
23{
24 if (connect(s, s_addr, addrlen) < 0) {
25 if (ENABLE_FEATURE_CLEAN_UP)
26 close(s);
27 if (s_addr->sa_family == AF_INET)
28 bb_perror_msg_and_die("%s (%s)",
29 "cannot connect to remote host",
30 inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr));
31 bb_perror_msg_and_die("cannot connect to remote host");
32 }
33}
34
11/* Return network byte ordered port number for a service. 35/* Return network byte ordered port number for a service.
12 * If "port" is a number use it as the port. 36 * If "port" is a number use it as the port.
13 * If "port" is a name it is looked up in /etc/services, if it isnt found return 37 * If "port" is a name it is looked up in /etc/services, if it isnt found return
14 * default_port 38 * default_port */
15 */ 39unsigned bb_lookup_port(const char *port, const char *protocol, unsigned default_port)
16unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port)
17{ 40{
18 unsigned short port_nr = htons(default_port); 41 unsigned port_nr = htons(default_port);
19 if (port) { 42 if (port) {
20 char *endptr;
21 int old_errno; 43 int old_errno;
22 long port_long;
23 44
24 /* Since this is a lib function, we're not allowed to reset errno to 0. 45 /* Since this is a lib function, we're not allowed to reset errno to 0.
25 * Doing so could break an app that is deferring checking of errno. */ 46 * Doing so could break an app that is deferring checking of errno. */
26 old_errno = errno; 47 old_errno = errno;
27 errno = 0; 48 port_nr = bb_strtou(port, NULL, 10);
28 port_long = strtol(port, &endptr, 10); 49 if (errno || port_nr > 65535) {
29 if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) {
30 struct servent *tserv = getservbyname(port, protocol); 50 struct servent *tserv = getservbyname(port, protocol);
31 if (tserv) { 51 if (tserv)
32 port_nr = tserv->s_port; 52 port_nr = tserv->s_port;
33 }
34 } else { 53 } else {
35 port_nr = htons(port_long); 54 port_nr = htons(port_nr);
36 } 55 }
37 errno = old_errno; 56 errno = old_errno;
38 } 57 }
39 return port_nr; 58 return port_nr;
40} 59}
41 60
61
62/* "Old" networking API - only IPv4 */
63
64
42void bb_lookup_host(struct sockaddr_in *s_in, const char *host) 65void bb_lookup_host(struct sockaddr_in *s_in, const char *host)
43{ 66{
44 struct hostent *he; 67 struct hostent *he;
@@ -49,18 +72,6 @@ void bb_lookup_host(struct sockaddr_in *s_in, const char *host)
49 memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length); 72 memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length);
50} 73}
51 74
52void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
53{
54 if (connect(s, s_addr, addrlen) < 0) {
55 if (ENABLE_FEATURE_CLEAN_UP) close(s);
56 if (s_addr->sa_family == AF_INET)
57 bb_perror_msg_and_die("%s (%s)",
58 "cannot connect to remote host",
59 inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr));
60 bb_perror_msg_and_die("cannot connect to remote host");
61 }
62}
63
64int xconnect_tcp_v4(struct sockaddr_in *s_addr) 75int xconnect_tcp_v4(struct sockaddr_in *s_addr)
65{ 76{
66 int s = xsocket(AF_INET, SOCK_STREAM, 0); 77 int s = xsocket(AF_INET, SOCK_STREAM, 0);
@@ -68,87 +79,144 @@ int xconnect_tcp_v4(struct sockaddr_in *s_addr)
68 return s; 79 return s;
69} 80}
70 81
71static const int one = 1;
72int setsockopt_reuseaddr(int fd)
73{
74 return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
75}
76int setsockopt_broadcast(int fd)
77{
78 return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one));
79}
80 82
81int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) 83/* "New" networking API */
82{
83 union {
84 struct in_addr a4;
85#if ENABLE_FEATURE_IPV6
86 struct in6_addr a6;
87#endif
88 } a;
89 84
90 /* TODO maybe: port spec? like n.n.n.n:nn */
91 85
86/* So far we do not expose struct and helpers to libbb */
87typedef struct len_and_sockaddr {
88 int len;
89 union {
90 struct sockaddr sa;
91 struct sockaddr_in sin;
92#if ENABLE_FEATURE_IPV6 92#if ENABLE_FEATURE_IPV6
93 if (socklen >= sizeof(struct sockaddr_in6) 93 struct sockaddr_in6 sin6;
94 && inet_pton(AF_INET6, dotted, &a.a6) > 0
95 ) {
96 ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6;
97 ((struct sockaddr_in6*)sp)->sin6_addr = a.a6;
98 /* ((struct sockaddr_in6*)sp)->sin6_port = */
99 return 0; /* success */
100 }
101#endif 94#endif
102 if (socklen >= sizeof(struct sockaddr_in) 95 };
103 && inet_pton(AF_INET, dotted, &a.a4) > 0 96} len_and_sockaddr;
104 ) { 97//extern int xsocket_stream_ip4or6(sa_family_t *fp);
105 ((struct sockaddr_in*)sp)->sin_family = AF_INET; 98//extern len_and_sockaddr* host2sockaddr(const char *host, int def_port);
106 ((struct sockaddr_in*)sp)->sin_addr = a.a4; 99//extern len_and_sockaddr* dotted2sockaddr(const char *dotted, int def_port);
107 /* ((struct sockaddr_in*)sp)->sin_port = */ 100
108 return 0; /* success */ 101/* peer: "1.2.3.4[:port]", "www.google.com[:port]"
102 * def_port: if neither of above specifies port #
103 */
104static len_and_sockaddr* str2sockaddr(const char *host, int def_port, int ai_flags)
105{
106 int rc;
107 len_and_sockaddr *r; // = NULL;
108 struct addrinfo *result = NULL;
109 const char *org_host = host; /* only for error msg */
110 const char *cp;
111 char service[sizeof(int)*3 + 1];
112 struct addrinfo hint;
113
114 /* Ugly parsing of host:addr */
115 if (ENABLE_FEATURE_IPV6 && host[0] == '[') {
116 host++;
117 cp = strchr(host, ']');
118 if (!cp || cp[1] != ':') /* Malformed: must have [xx]:nn */
119 bb_error_msg_and_die("bad address '%s'", org_host);
120 //return r; /* return NULL */
121 } else {
122 cp = strrchr(host, ':');
123 if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) {
124 /* There is more than one ':' (e.g. "::1") */
125 cp = NULL; /* it's not a port spec */
126 }
109 } 127 }
110 return 1; 128 if (cp) {
129 host = safe_strncpy(alloca(cp - host + 1), host, cp - host);
130 if (ENABLE_FEATURE_IPV6 && *cp != ':')
131 cp++; /* skip ']' */
132 cp++; /* skip ':' */
133 } else {
134 utoa_to_buf(def_port, service, sizeof(service));
135 cp = service;
136 }
137
138 memset(&hint, 0 , sizeof(hint));
139 /* hint.ai_family = AF_UNSPEC; - zero anyway */
140#if !ENABLE_FEATURE_IPV6
141 hint.ai_family = AF_INET; /* do not try to find IPv6 */
142#endif
143 /* Needed. Or else we will get each address thrice (or more)
144 * for each possible socket type (tcp,udp,raw...): */
145 hint.ai_socktype = SOCK_STREAM;
146 hint.ai_flags = ai_flags | AI_NUMERICSERV;
147 rc = getaddrinfo(host, cp, &hint, &result);
148 if (rc || !result)
149 bb_error_msg_and_die("bad address '%s'", org_host);
150 r = xmalloc(offsetof(len_and_sockaddr, sa) + result->ai_addrlen);
151 r->len = result->ai_addrlen;
152 memcpy(&r->sa, result->ai_addr, result->ai_addrlen);
153 freeaddrinfo(result);
154 return r;
155}
156
157static len_and_sockaddr* host2sockaddr(const char *host, int def_port)
158{
159 return str2sockaddr(host, def_port, 0);
160}
161
162static len_and_sockaddr* dotted2sockaddr(const char *host, int def_port)
163{
164 return str2sockaddr(host, def_port, NI_NUMERICHOST);
111} 165}
112 166
113int xsocket_stream_ip4or6(sa_family_t *fp) 167static int xsocket_stream_ip4or6(len_and_sockaddr *lsa)
114{ 168{
115 int fd; 169 int fd;
116#if ENABLE_FEATURE_IPV6 170#if ENABLE_FEATURE_IPV6
117 fd = socket(AF_INET6, SOCK_STREAM, 0); 171 fd = socket(AF_INET6, SOCK_STREAM, 0);
118 if (fp) *fp = AF_INET6; 172 lsa->sa.sa_family = AF_INET6;
119 if (fd < 0) 173 lsa->len = sizeof(struct sockaddr_in6);
174 if (fd >= 0)
175 return fd;
120#endif 176#endif
121 { 177 fd = xsocket(AF_INET, SOCK_STREAM, 0);
122 fd = xsocket(AF_INET, SOCK_STREAM, 0); 178 lsa->sa.sa_family = AF_INET;
123 if (fp) *fp = AF_INET; 179 lsa->len = sizeof(struct sockaddr_in);
124 }
125 return fd; 180 return fd;
126} 181}
127 182
128int create_and_bind_socket_ip4or6(const char *hostaddr, int port) 183int create_and_bind_stream_or_die(const char *bindaddr, int port)
129{ 184{
130 int fd; 185 int fd;
131 sockaddr_inet sa; 186 len_and_sockaddr *lsa;
132 187
133 memset(&sa, 0, sizeof(sa)); 188 if (bindaddr) {
134 if (hostaddr) { 189 lsa = dotted2sockaddr(bindaddr, port);
135 if (dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) 190 /* currently NULL check is in str2sockaddr */
136 bb_error_msg_and_die("bad address '%s'", hostaddr); 191 //if (!lsa)
192 // bb_error_msg_and_die("bad address '%s'", bindaddr);
137 /* user specified bind addr dictates family */ 193 /* user specified bind addr dictates family */
138 fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); 194 fd = xsocket(lsa->sa.sa_family, SOCK_STREAM, 0);
139 } else 195 } else {
140 fd = xsocket_stream_ip4or6(&sa.sa.sa_family); 196 lsa = xzalloc(offsetof(len_and_sockaddr, sa) +
197 USE_FEATURE_IPV6(sizeof(struct sockaddr_in6))
198 SKIP_FEATURE_IPV6(sizeof(struct sockaddr_in))
199 );
200 fd = xsocket_stream_ip4or6(lsa);
201 }
141 setsockopt_reuseaddr(fd); 202 setsockopt_reuseaddr(fd);
203 xbind(fd, &lsa->sa, lsa->len);
204 free(lsa);
205 return fd;
206}
142 207
143 /* if (port >= 0) { */ 208int create_and_connect_stream_or_die(const char *peer, int port)
144#if ENABLE_FEATURE_IPV6 209{
145 if (sa.sa.sa_family == AF_INET6 /* && !sa.sin6.sin6_port */) 210 int fd;
146 sa.sin6.sin6_port = htons(port); 211 len_and_sockaddr *lsa;
147#endif
148 if (sa.sa.sa_family == AF_INET /* && !sa.sin.sin_port */)
149 sa.sin.sin_port = htons(port);
150 /* } */
151 212
152 xbind(fd, &sa.sa, sizeof(sa)); 213 lsa = host2sockaddr(peer, port);
214 /* currently NULL check is in str2sockaddr */
215 //if (!lsa)
216 // bb_error_msg_and_die("bad address '%s'", peer);
217 fd = xsocket(lsa->sa.sa_family, SOCK_STREAM, 0);
218 setsockopt_reuseaddr(fd);
219 xconnect(fd, &lsa->sa, lsa->len);
220 free(lsa);
153 return fd; 221 return fd;
154} 222}