diff options
Diffstat (limited to 'libbb/xconnect.c')
-rw-r--r-- | libbb/xconnect.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/libbb/xconnect.c b/libbb/xconnect.c new file mode 100644 index 000000000..b85648007 --- /dev/null +++ b/libbb/xconnect.c | |||
@@ -0,0 +1,153 @@ | |||
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 | */ | ||
8 | |||
9 | #include "libbb.h" | ||
10 | |||
11 | /* Return network byte ordered port number for a service. | ||
12 | * 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 | ||
14 | * default_port | ||
15 | */ | ||
16 | unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port) | ||
17 | { | ||
18 | unsigned short port_nr = htons(default_port); | ||
19 | if (port) { | ||
20 | char *endptr; | ||
21 | int old_errno; | ||
22 | long port_long; | ||
23 | |||
24 | /* 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. */ | ||
26 | old_errno = errno; | ||
27 | errno = 0; | ||
28 | port_long = strtol(port, &endptr, 10); | ||
29 | if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) { | ||
30 | struct servent *tserv = getservbyname(port, protocol); | ||
31 | if (tserv) { | ||
32 | port_nr = tserv->s_port; | ||
33 | } | ||
34 | } else { | ||
35 | port_nr = htons(port_long); | ||
36 | } | ||
37 | errno = old_errno; | ||
38 | } | ||
39 | return port_nr; | ||
40 | } | ||
41 | |||
42 | void bb_lookup_host(struct sockaddr_in *s_in, const char *host) | ||
43 | { | ||
44 | struct hostent *he; | ||
45 | |||
46 | memset(s_in, 0, sizeof(struct sockaddr_in)); | ||
47 | s_in->sin_family = AF_INET; | ||
48 | he = xgethostbyname(host); | ||
49 | memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length); | ||
50 | } | ||
51 | |||
52 | void 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("cannot connect to remote host (%s)", | ||
58 | inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); | ||
59 | bb_perror_msg_and_die("cannot connect to remote host"); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | int xconnect_tcp_v4(struct sockaddr_in *s_addr) | ||
64 | { | ||
65 | int s = xsocket(AF_INET, SOCK_STREAM, 0); | ||
66 | xconnect(s, (struct sockaddr*) s_addr, sizeof(*s_addr)); | ||
67 | return s; | ||
68 | } | ||
69 | |||
70 | static const int one = 1; | ||
71 | int setsockopt_reuseaddr(int fd) | ||
72 | { | ||
73 | return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | ||
74 | } | ||
75 | int setsockopt_broadcast(int fd) | ||
76 | { | ||
77 | return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)); | ||
78 | } | ||
79 | |||
80 | int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) | ||
81 | { | ||
82 | union { | ||
83 | struct in_addr a4; | ||
84 | #if ENABLE_FEATURE_IPV6 | ||
85 | struct in6_addr a6; | ||
86 | #endif | ||
87 | } a; | ||
88 | |||
89 | /* TODO maybe: port spec? like n.n.n.n:nn */ | ||
90 | |||
91 | #if ENABLE_FEATURE_IPV6 | ||
92 | if (socklen >= sizeof(struct sockaddr_in6) | ||
93 | && inet_pton(AF_INET6, dotted, &a.a6) > 0 | ||
94 | ) { | ||
95 | ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6; | ||
96 | ((struct sockaddr_in6*)sp)->sin6_addr = a.a6; | ||
97 | /* ((struct sockaddr_in6*)sp)->sin6_port = */ | ||
98 | return 0; /* success */ | ||
99 | } | ||
100 | #endif | ||
101 | if (socklen >= sizeof(struct sockaddr_in) | ||
102 | && inet_pton(AF_INET, dotted, &a.a4) > 0 | ||
103 | ) { | ||
104 | ((struct sockaddr_in*)sp)->sin_family = AF_INET; | ||
105 | ((struct sockaddr_in*)sp)->sin_addr = a.a4; | ||
106 | /* ((struct sockaddr_in*)sp)->sin_port = */ | ||
107 | return 0; /* success */ | ||
108 | } | ||
109 | return 1; | ||
110 | } | ||
111 | |||
112 | int xsocket_stream_ip4or6(sa_family_t *fp) | ||
113 | { | ||
114 | int fd; | ||
115 | #if ENABLE_FEATURE_IPV6 | ||
116 | fd = socket(AF_INET6, SOCK_STREAM, 0); | ||
117 | if (fp) *fp = AF_INET6; | ||
118 | if (fd < 0) | ||
119 | #endif | ||
120 | { | ||
121 | fd = xsocket(AF_INET, SOCK_STREAM, 0); | ||
122 | if (fp) *fp = AF_INET; | ||
123 | } | ||
124 | return fd; | ||
125 | } | ||
126 | |||
127 | int create_and_bind_socket_ip4or6(const char *hostaddr, int port) | ||
128 | { | ||
129 | int fd; | ||
130 | sockaddr_inet sa; | ||
131 | |||
132 | memset(&sa, 0, sizeof(sa)); | ||
133 | if (hostaddr) { | ||
134 | if (dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) | ||
135 | bb_error_msg_and_die("bad address '%s'", hostaddr); | ||
136 | /* user specified bind addr dictates family */ | ||
137 | fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); | ||
138 | } else | ||
139 | fd = xsocket_stream_ip4or6(&sa.sa.sa_family); | ||
140 | setsockopt_reuseaddr(fd); | ||
141 | |||
142 | /* if (port >= 0) { */ | ||
143 | #if ENABLE_FEATURE_IPV6 | ||
144 | if (sa.sa.sa_family == AF_INET6 /* && !sa.sin6.sin6_port */) | ||
145 | sa.sin6.sin6_port = htons(port); | ||
146 | #endif | ||
147 | if (sa.sa.sa_family == AF_INET /* && !sa.sin.sin_port */) | ||
148 | sa.sin.sin_port = htons(port); | ||
149 | /* } */ | ||
150 | |||
151 | xbind(fd, &sa.sa, sizeof(sa)); | ||
152 | return fd; | ||
153 | } | ||