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 <errno.h>
28 #include <unistd.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netinet/tcp.h>
32 #include <netdb.h>
33 #include <fcntl.h>
34 #endif
35
36 #include "sock.h"
37 #include "snprintf.h"
38
sock_initialize(void)39 void sock_initialize(void)
40 {
41 #ifdef _WIN32
42 WSADATA wsad;
43 WSAStartup(0x0101, &wsad);
44 #endif
45 }
46
sock_shutdown(void)47 void sock_shutdown(void)
48 {
49 #ifdef _WIN32
50 WSACleanup();
51 #endif
52 }
53
sock_error(void)54 int sock_error(void)
55 {
56 #ifdef _WIN32
57 return WSAGetLastError();
58 #else
59 return errno;
60 #endif
61 }
62
_in_progress(int error)63 static int _in_progress(int error)
64 {
65 #ifdef _WIN32
66 return (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS);
67 #else
68 return (error == EINPROGRESS);
69 #endif
70 }
71
sock_connect(const char * host,unsigned short port)72 sock_t sock_connect(const char *host, unsigned short port)
73 {
74 sock_t sock;
75 char service[6];
76 struct addrinfo *res, *ainfo, hints;
77 int err;
78
79 xmpp_snprintf(service, 6, "%u", port);
80
81 memset(&hints, 0, sizeof(struct addrinfo));
82 hints.ai_family = AF_UNSPEC;
83 #ifdef AI_ADDRCONFIG
84 hints.ai_flags = AI_ADDRCONFIG;
85 #endif /* AI_ADDRCONFIG */
86 hints.ai_protocol = IPPROTO_TCP;
87 hints.ai_socktype = SOCK_STREAM;
88
89 err = getaddrinfo(host, service, &hints, &res);
90 if (err != 0)
91 return -1;
92
93 for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
94 sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
95 if (sock < 0)
96 continue;
97
98 err = sock_set_nonblocking(sock);
99 if (err == 0) {
100 err = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
101 if (err == 0 || _in_progress(sock_error()))
102 break;
103 }
104 sock_close(sock);
105 }
106 freeaddrinfo(res);
107 sock = ainfo == NULL ? -1 : sock;
108
109 return sock;
110 }
111
sock_set_keepalive(sock_t sock,int timeout,int interval)112 int sock_set_keepalive(sock_t sock, int timeout, int interval)
113 {
114 int ret;
115 int optval = (timeout && interval) ? 1 : 0;
116
117 /* This function doesn't change maximum number of keepalive probes */
118
119 #ifdef _WIN32
120 struct tcp_keepalive ka;
121 DWORD dw = 0;
122
123 ka.onoff = optval;
124 ka.keepalivetime = timeout * 1000;
125 ka.keepaliveinterval = interval * 1000;
126 ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
127 NULL, NULL);
128 #else
129 ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
130 if (ret < 0)
131 return ret;
132
133 if (optval) {
134 #ifdef TCP_KEEPIDLE
135 ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
136 sizeof(timeout));
137 #elif defined(TCP_KEEPALIVE)
138 /* QNX receives `struct timeval' as argument, but it seems OSX does int
139 */
140 ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
141 sizeof(timeout));
142 #endif /* TCP_KEEPIDLE */
143 if (ret < 0)
144 return ret;
145 #ifdef TCP_KEEPINTVL
146 ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
147 sizeof(interval));
148 if (ret < 0)
149 return ret;
150 #endif /* TCP_KEEPINTVL */
151 }
152 #endif /* _WIN32 */
153
154 return ret;
155 }
156
sock_close(sock_t sock)157 int sock_close(sock_t sock)
158 {
159 #ifdef _WIN32
160 return closesocket(sock);
161 #else
162 return close(sock);
163 #endif
164 }
165
_sock_set_blocking_mode(sock_t sock,int blocking)166 static int _sock_set_blocking_mode(sock_t sock, int blocking)
167 {
168 #ifdef _WIN32
169 u_long nonblock = blocking ? 0 : 1;
170 return ioctlsocket(sock, FIONBIO, &nonblock);
171 #else
172 int rc;
173
174 rc = fcntl(sock, F_GETFL, NULL);
175 if (rc >= 0) {
176 rc = blocking ? rc & (~O_NONBLOCK) : rc | O_NONBLOCK;
177 rc = fcntl(sock, F_SETFL, rc);
178 }
179 return rc;
180 #endif
181 }
182
sock_set_blocking(sock_t sock)183 int sock_set_blocking(sock_t sock)
184 {
185 return _sock_set_blocking_mode(sock, 1);
186 }
187
sock_set_nonblocking(sock_t sock)188 int sock_set_nonblocking(sock_t sock)
189 {
190 return _sock_set_blocking_mode(sock, 0);
191 }
192
sock_read(sock_t sock,void * buff,size_t len)193 int sock_read(sock_t sock, void *buff, size_t len)
194 {
195 return recv(sock, buff, len, 0);
196 }
197
sock_write(sock_t sock,const void * buff,size_t len)198 int sock_write(sock_t sock, const void *buff, size_t len)
199 {
200 return send(sock, buff, len, 0);
201 }
202
sock_is_recoverable(int error)203 int sock_is_recoverable(int error)
204 {
205 #ifdef _WIN32
206 return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
207 error == WSAEINPROGRESS);
208 #else
209 return (error == EAGAIN || error == EINTR);
210 #endif
211 }
212
sock_connect_error(sock_t sock)213 int sock_connect_error(sock_t sock)
214 {
215 struct sockaddr_storage ss;
216 struct sockaddr *sa = (struct sockaddr *)&ss;
217 socklen_t len;
218 char temp;
219
220 memset(&ss, 0, sizeof(ss));
221 len = sizeof(ss);
222 sa->sa_family = AF_UNSPEC;
223
224 /* we don't actually care about the peer name, we're just checking if
225 * we're connected or not */
226 if (getpeername(sock, sa, &len) == 0) {
227 return 0;
228 }
229
230 /* it's possible that the error wasn't ENOTCONN, so if it wasn't,
231 * return that */
232 #ifdef _WIN32
233 if (sock_error() != WSAENOTCONN)
234 return sock_error();
235 #else
236 if (sock_error() != ENOTCONN)
237 return sock_error();
238 #endif
239
240 /* load the correct error into errno through error slippage */
241 recv(sock, &temp, 1, 0);
242
243 return sock_error();
244 }
245