Line data Source code
1 : /* sock.c
2 : ** strophe XMPP client library -- socket abstraction implementation
3 : **
4 : ** Copyright (C) 2005-2009 Collecta, Inc.
5 : **
6 : ** This software is provided AS-IS with no warranty, either express
7 : ** or implied.
8 : **
9 : ** This program is dual licensed under the MIT and GPLv3 licenses.
10 : */
11 :
12 : /** @file
13 : * Socket abstraction.
14 : */
15 :
16 : #include <stdio.h>
17 : #include <stdlib.h>
18 : #include <string.h>
19 : #include <sys/types.h>
20 :
21 : #ifdef _WIN32
22 : #include <winsock2.h>
23 : #include <ws2tcpip.h>
24 : #include <iphlpapi.h>
25 : #include <mstcpip.h> /* tcp_keepalive */
26 : #else
27 : #include <arpa/inet.h>
28 : #include <errno.h>
29 : #include <unistd.h>
30 : #include <sys/socket.h>
31 : #include <netinet/in.h>
32 : #include <netinet/tcp.h>
33 : #include <netdb.h>
34 : #include <fcntl.h>
35 : #endif
36 :
37 : #include "common.h"
38 : #include "resolver.h"
39 :
40 : struct _xmpp_sock_t {
41 : xmpp_ctx_t *ctx;
42 : xmpp_conn_t *conn;
43 : struct addrinfo *ainfo_list;
44 : struct addrinfo *ainfo_cur;
45 : resolver_srv_rr_t *srv_rr_list;
46 : resolver_srv_rr_t *srv_rr_cur;
47 : const char *host;
48 : unsigned short port;
49 : };
50 :
51 2 : void sock_initialize(void)
52 : {
53 : #ifdef _WIN32
54 : WSADATA wsad;
55 : WSAStartup(0x0101, &wsad);
56 : #endif
57 2 : }
58 :
59 2 : void sock_shutdown(void)
60 : {
61 : #ifdef _WIN32
62 : WSACleanup();
63 : #endif
64 2 : }
65 :
66 0 : int sock_error(void)
67 : {
68 : #ifdef _WIN32
69 : return WSAGetLastError();
70 : #else
71 0 : return errno;
72 : #endif
73 : }
74 :
75 : static int _in_progress(int error)
76 : {
77 : #ifdef _WIN32
78 : return (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS);
79 : #else
80 : return (error == EINPROGRESS);
81 : #endif
82 : }
83 :
84 0 : static void sock_getaddrinfo(xmpp_sock_t *xsock)
85 : {
86 0 : char service[6];
87 0 : struct addrinfo hints;
88 0 : int rc;
89 :
90 0 : if (xsock->ainfo_list) {
91 0 : freeaddrinfo(xsock->ainfo_list);
92 0 : xsock->ainfo_list = NULL;
93 : }
94 :
95 0 : if (xsock->srv_rr_cur) {
96 : /* Cache host and port for debug logs. */
97 0 : xsock->host = xsock->srv_rr_cur->target;
98 0 : xsock->port = xsock->srv_rr_cur->port;
99 :
100 0 : strophe_snprintf(service, 6, "%u", xsock->srv_rr_cur->port);
101 0 : memset(&hints, 0, sizeof(struct addrinfo));
102 0 : hints.ai_family = AF_UNSPEC;
103 : #ifdef AI_ADDRCONFIG
104 0 : hints.ai_flags = AI_ADDRCONFIG;
105 : #endif /* AI_ADDRCONFIG */
106 0 : hints.ai_protocol = IPPROTO_TCP;
107 0 : hints.ai_socktype = SOCK_STREAM;
108 :
109 0 : rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints,
110 : &xsock->ainfo_list);
111 0 : if (rc != 0) {
112 0 : strophe_debug(xsock->ctx, "sock", "getaddrinfo() failed with %d",
113 : rc);
114 0 : xsock->ainfo_list = NULL;
115 : }
116 : }
117 :
118 0 : xsock->ainfo_cur = xsock->ainfo_list;
119 0 : }
120 :
121 0 : xmpp_sock_t *sock_new(xmpp_conn_t *conn,
122 : const char *domain,
123 : const char *host,
124 : unsigned short port)
125 : {
126 0 : xmpp_ctx_t *ctx = conn->ctx;
127 0 : xmpp_sock_t *xsock;
128 0 : int found = XMPP_DOMAIN_NOT_FOUND;
129 :
130 0 : xsock = strophe_alloc(ctx, sizeof(*xsock));
131 0 : if (!xsock) {
132 : return NULL;
133 : }
134 :
135 0 : xsock->ctx = ctx;
136 0 : xsock->conn = conn;
137 0 : xsock->host = NULL;
138 0 : xsock->port = 0;
139 :
140 0 : if (!host) {
141 0 : found = resolver_srv_lookup(ctx, "xmpp-client", "tcp", domain,
142 : &xsock->srv_rr_list);
143 0 : if (XMPP_DOMAIN_NOT_FOUND == found)
144 0 : strophe_debug(ctx, "sock",
145 : "SRV lookup failed, connecting via domain.");
146 : }
147 0 : if (XMPP_DOMAIN_NOT_FOUND == found) {
148 : /* Resolution failed or the host is provided explicitly. */
149 0 : xsock->srv_rr_list =
150 0 : resolver_srv_rr_new(ctx, host ? host : domain, port, 0, 0);
151 : }
152 0 : xsock->srv_rr_cur = xsock->srv_rr_list;
153 :
154 0 : xsock->ainfo_list = NULL;
155 0 : sock_getaddrinfo(xsock);
156 0 : if (xsock->srv_rr_cur)
157 0 : xsock->srv_rr_cur = xsock->srv_rr_cur->next;
158 :
159 : return xsock;
160 : }
161 :
162 8 : void sock_free(xmpp_sock_t *xsock)
163 : {
164 8 : if (!xsock)
165 : return;
166 :
167 0 : if (xsock->ainfo_list)
168 0 : freeaddrinfo(xsock->ainfo_list);
169 0 : if (xsock->srv_rr_list)
170 0 : resolver_srv_free(xsock->ctx, xsock->srv_rr_list);
171 0 : strophe_free(xsock->ctx, xsock);
172 : }
173 :
174 0 : static const char *_sockaddr2str(struct sockaddr *sa, char *buf, size_t buflen)
175 : {
176 0 : buf[0] = '\0';
177 :
178 0 : switch (sa->sa_family) {
179 0 : case AF_INET:
180 0 : inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf, buflen);
181 0 : break;
182 0 : case AF_INET6:
183 0 : inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf,
184 : buflen);
185 0 : break;
186 : default:
187 0 : strophe_snprintf(buf, buflen, "<Unknown>");
188 : }
189 0 : return buf;
190 : }
191 :
192 0 : sock_t sock_connect(xmpp_sock_t *xsock)
193 : {
194 0 : struct addrinfo *ainfo;
195 0 : sock_t sock;
196 0 : int rc = 0;
197 0 : char buf[64];
198 :
199 0 : do {
200 0 : if (!xsock->ainfo_cur) {
201 0 : sock_getaddrinfo(xsock);
202 0 : if (xsock->srv_rr_cur)
203 0 : xsock->srv_rr_cur = xsock->srv_rr_cur->next;
204 : }
205 0 : if (!xsock->ainfo_cur) {
206 : /* We tried all available addresses. */
207 0 : return INVALID_SOCKET;
208 : }
209 :
210 0 : ainfo = xsock->ainfo_cur;
211 0 : strophe_debug(xsock->ctx, "sock", "Connecting to %s:%u via %s",
212 0 : xsock->host, xsock->port,
213 : _sockaddr2str(ainfo->ai_addr, buf, sizeof(buf)));
214 :
215 0 : sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
216 0 : if (sock != INVALID_SOCKET) {
217 0 : if (xsock->conn->sockopt_cb) {
218 : /* Don't allow user to overwrite sockfd value. */
219 0 : sock_t sock_copy = sock;
220 0 : rc = xsock->conn->sockopt_cb(xsock->conn, &sock_copy);
221 0 : if (rc != 0) {
222 0 : strophe_debug(xsock->ctx, "sock",
223 : "User's setsockopt callback"
224 : "failed with %d (errno=%d)",
225 0 : rc, errno);
226 : }
227 : }
228 0 : if (rc == 0)
229 0 : rc = sock_set_nonblocking(sock);
230 0 : if (rc == 0)
231 0 : rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
232 : /* Assume only connect() can cause "in progress" error. */
233 0 : if (rc != 0 && !_in_progress(sock_error())) {
234 0 : sock_close(sock);
235 : sock = INVALID_SOCKET;
236 : }
237 : }
238 0 : strophe_debug(xsock->ctx, "sock", "sock_connect() result %d", sock);
239 :
240 0 : xsock->ainfo_cur = xsock->ainfo_cur->ai_next;
241 0 : } while (sock == INVALID_SOCKET);
242 :
243 : return sock;
244 : }
245 :
246 0 : int sock_set_keepalive(sock_t sock,
247 : int timeout,
248 : int interval,
249 : int count,
250 : unsigned int user_timeout)
251 : {
252 0 : int ret;
253 0 : int optval = (timeout && interval) ? 1 : 0;
254 :
255 0 : UNUSED(count);
256 0 : UNUSED(user_timeout);
257 :
258 : #ifdef _WIN32
259 : struct tcp_keepalive ka;
260 : DWORD dw = 0;
261 :
262 : ka.onoff = optval;
263 : ka.keepalivetime = timeout * 1000;
264 : ka.keepaliveinterval = interval * 1000;
265 : ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
266 : NULL, NULL);
267 : #else
268 0 : ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
269 0 : if (ret < 0)
270 : return ret;
271 :
272 0 : if (optval) {
273 : #ifdef TCP_KEEPIDLE
274 0 : ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
275 : sizeof(timeout));
276 : #elif defined(TCP_KEEPALIVE)
277 : /* QNX receives `struct timeval' as argument, but it seems OSX does int
278 : */
279 : ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
280 : sizeof(timeout));
281 : #endif /* TCP_KEEPIDLE */
282 0 : if (ret < 0)
283 : return ret;
284 : #ifdef TCP_KEEPINTVL
285 0 : ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
286 : sizeof(interval));
287 0 : if (ret < 0)
288 : return ret;
289 : #endif /* TCP_KEEPINTVL */
290 : }
291 :
292 0 : if (count) {
293 : #ifdef TCP_KEEPCNT
294 0 : ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
295 0 : if (ret < 0)
296 : return ret;
297 : #endif /* TCP_KEEPCNT */
298 : }
299 :
300 0 : if (user_timeout) {
301 : #ifdef TCP_USER_TIMEOUT
302 0 : ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
303 : sizeof(user_timeout));
304 0 : if (ret < 0)
305 : return ret;
306 : #elif defined(TCP_RXT_CONNDROPTIME)
307 : int rxt = user_timeout / 1000;
308 : ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
309 : sizeof(rxt));
310 : if (ret < 0)
311 : return ret;
312 : #endif /* TCP_USER_TIMEOUT */
313 : }
314 :
315 : #endif /* _WIN32 */
316 :
317 : return ret;
318 : }
319 :
320 : /** Example sockopt callback function
321 : * An example function that can be used to set reasonable default keepalive
322 : * options on sockets when registered for a connection with
323 : * xmpp_conn_set_sockopt_callback()
324 : *
325 : * @param conn a Strophe connection object
326 : * @param socket pointer to a socket descriptor
327 : *
328 : * @see xmpp_sockopt_callback for details on the `socket` parameter
329 : * @ingroup Connections
330 : */
331 0 : int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *socket)
332 : {
333 0 : sock_t sock = *((sock_t *)socket);
334 :
335 0 : return sock_set_keepalive(
336 : sock, conn->ka_timeout, conn->ka_interval, conn->ka_count,
337 0 : conn->ka_count
338 0 : ? (conn->ka_timeout + conn->ka_interval * conn->ka_count) * 1000
339 : : 0);
340 : }
341 :
342 0 : int sock_close(sock_t sock)
343 : {
344 : #ifdef _WIN32
345 : return closesocket(sock);
346 : #else
347 0 : return close(sock);
348 : #endif
349 : }
350 :
351 0 : static int _sock_set_blocking_mode(sock_t sock, int blocking)
352 : {
353 : #ifdef _WIN32
354 : u_long nonblock = blocking ? 0 : 1;
355 : return ioctlsocket(sock, FIONBIO, &nonblock);
356 : #else
357 0 : int rc;
358 :
359 0 : rc = fcntl(sock, F_GETFL, NULL);
360 0 : if (rc >= 0) {
361 0 : rc = blocking ? rc & (~O_NONBLOCK) : rc | O_NONBLOCK;
362 0 : rc = fcntl(sock, F_SETFL, rc);
363 : }
364 0 : return rc;
365 : #endif
366 : }
367 :
368 0 : int sock_set_blocking(sock_t sock)
369 : {
370 0 : return _sock_set_blocking_mode(sock, 1);
371 : }
372 :
373 0 : int sock_set_nonblocking(sock_t sock)
374 : {
375 0 : return _sock_set_blocking_mode(sock, 0);
376 : }
377 :
378 0 : int sock_read(sock_t sock, void *buff, size_t len)
379 : {
380 0 : return recv(sock, buff, len, 0);
381 : }
382 :
383 0 : int sock_write(sock_t sock, const void *buff, size_t len)
384 : {
385 0 : return send(sock, buff, len, 0);
386 : }
387 :
388 0 : int sock_is_recoverable(int error)
389 : {
390 : #ifdef _WIN32
391 : return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
392 : error == WSAEINPROGRESS);
393 : #else
394 0 : return (error == EAGAIN || error == EINTR);
395 : #endif
396 : }
397 :
398 0 : int sock_connect_error(sock_t sock)
399 : {
400 0 : struct sockaddr_storage ss;
401 0 : struct sockaddr *sa = (struct sockaddr *)&ss;
402 0 : socklen_t len;
403 0 : char temp;
404 :
405 0 : memset(&ss, 0, sizeof(ss));
406 0 : len = sizeof(ss);
407 0 : sa->sa_family = AF_UNSPEC;
408 :
409 : /* we don't actually care about the peer name, we're just checking if
410 : * we're connected or not */
411 0 : if (getpeername(sock, sa, &len) == 0) {
412 0 : return 0;
413 : }
414 :
415 : /* it's possible that the error wasn't ENOTCONN, so if it wasn't,
416 : * return that */
417 : #ifdef _WIN32
418 : if (sock_error() != WSAENOTCONN)
419 : return sock_error();
420 : #else
421 0 : if (sock_error() != ENOTCONN)
422 0 : return sock_error();
423 : #endif
424 :
425 : /* load the correct error into errno through error slippage */
426 0 : recv(sock, &temp, 1, 0);
427 :
428 0 : return sock_error();
429 : }
|