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