1 /*
2 $Id: cddb_net.c,v 1.19 2009/03/01 03:28:07 jcaratzas Exp $
3
4 Copyright (C) 2003, 2004, 2005 Kris Verbeeck <airborne@advalvas.be>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with this library; if not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20 */
21
22 #include "cddb/cddb_ni.h"
23
24 #include <errno.h>
25
26 #ifdef HAVE_FCNTL_H
27 #include <fcntl.h>
28 #endif
29
30 #ifdef HAVE_NETDB_H
31 #include <netdb.h>
32 #endif
33
34 #include <setjmp.h>
35 #include <signal.h>
36
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
40
41 #ifdef HAVE_TIME_H
42 #include <time.h>
43 #endif
44 #if defined(HAVE_SYS_TIME_H) && defined(TIME_WITH_SYS_TIME)
45 #include <sys/time.h>
46 #endif
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51
52 #ifdef HAVE_SYS_SELECT_H
53 #include <sys/select.h>
54 #endif
55
56 #ifdef HAVE_SYS_SOCKET_H
57 #include <sys/socket.h>
58 #endif
59
60 #ifdef HAVE_SYS_TYPES_H
61 #include <sys/types.h>
62 #endif
63
64
65 /* Utility functions */
66
67
68 /**
69 * Checks whether bytes can be read/written from/to the socket within
70 * the specified time out period.
71 *
72 * @param sock The socket to read from.
73 * @param timeout Number of seconds after which to time out.
74 * @param to_write TRUE if we have to check for writing, FALSE for
75 * reading.
76 * @return TRUE if reading/writing is possible, FALSE otherwise.
77 */
sock_ready(int sock,int timeout,int to_write)78 static int sock_ready(int sock, int timeout, int to_write)
79 {
80 fd_set fds;
81 struct timeval tv;
82 int rv;
83
84 //cddb_log_debug("sock_ready()");
85 /* set up select time out */
86 tv.tv_sec = timeout;
87 tv.tv_usec = 0;
88 /* set up file descriptor set */
89 FD_ZERO(&fds);
90 FD_SET(sock, &fds);
91 /* wait for data to become available */
92 if (to_write) {
93 rv = select(sock + 1, NULL, &fds, NULL, &tv) ;
94 } else {
95 rv = select(sock + 1, &fds, NULL, NULL, &tv) ;
96 }
97 if (rv <= 0) {
98 if (rv == 0) {
99 errno = ETIMEDOUT;
100 }
101 return FALSE;
102 }
103 return TRUE;
104 }
105 #define sock_can_read(s,t) sock_ready(s, t, FALSE)
106 #define sock_can_write(s,t) sock_ready(s, t, TRUE)
107
108
109 /* Socket-based work-alikes */
110
111
sock_fgets(char * s,int size,cddb_conn_t * c)112 char *sock_fgets(char *s, int size, cddb_conn_t *c)
113 {
114 int rv;
115 time_t now, end, timeout;
116 char *p = s;
117
118 cddb_log_debug("sock_fgets()");
119 timeout = c->timeout;
120 end = time(NULL) + timeout;
121 size--; /* save one for terminating null */
122 while (size) {
123 now = time(NULL);
124 timeout = end - now;
125 if (timeout <= 0) {
126 errno = ETIMEDOUT;
127 return NULL; /* time out */
128 }
129 /* can we read from the socket? */
130 if (!sock_can_read(c->socket, timeout)) {
131 /* error or time out */
132 return NULL;
133 }
134 /* read one byte */
135 rv = recv(c->socket, p, 1, 0);
136 if (rv == -1) {
137 /* recv() error */
138 return NULL;
139 } else if (rv == 0) {
140 /* EOS reached */
141 break;
142 } else if (*p == CHR_LF) {
143 /* EOL reached, stop reading */
144 p++;
145 break;
146 }
147 p++;
148 size--;
149 }
150 if (p == s) {
151 cddb_log_debug("...read = Empty");
152 return NULL;
153 }
154 *p = CHR_EOS;
155 cddb_log_debug("...read = '%s'", s);
156 return s;
157 }
158
sock_fwrite(const void * ptr,size_t size,size_t nmemb,cddb_conn_t * c)159 size_t sock_fwrite(const void *ptr, size_t size, size_t nmemb, cddb_conn_t *c)
160 {
161 size_t total_size, to_send;
162 time_t now, end, timeout;
163 int rv;
164 const char *p = (const char *)ptr;
165
166 cddb_log_debug("sock_fwrite()");
167 total_size = size * nmemb;
168 to_send = total_size;
169 timeout = c->timeout;
170 end = time(NULL) + timeout;
171 while (to_send) {
172 now = time(NULL);
173 timeout = end - now;
174 if (timeout <= 0) {
175 /* time out */
176 errno = ETIMEDOUT;
177 break;
178 }
179 /* can we write to the socket? */
180 if (!sock_can_write(c->socket, timeout)) {
181 /* error or time out */
182 break;
183 }
184 /* try sending data */
185 rv = send(c->socket, p, to_send, 0);
186 if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
187 /* error */
188 break;
189 } else {
190 to_send -= rv;
191 p += rv;
192 }
193 }
194 return (total_size - to_send) / size;
195 }
196
sock_fprintf(cddb_conn_t * c,const char * format,...)197 int sock_fprintf(cddb_conn_t *c, const char *format, ...)
198 {
199 int rv;
200 va_list args;
201
202 cddb_log_debug("sock_fprintf()");
203 va_start(args, format);
204 rv = sock_vfprintf(c, format, args);
205 va_end(args);
206 return rv;
207 }
208
sock_vfprintf(cddb_conn_t * c,const char * format,va_list ap)209 int sock_vfprintf(cddb_conn_t *c, const char *format, va_list ap)
210 {
211 char *buf;
212 int rv;
213
214 cddb_log_debug("sock_vfprintf()");
215 buf = (char*)malloc(c->buf_size);
216 rv = vsnprintf(buf, c->buf_size, format, ap);
217 cddb_log_debug("...buf = '%s'", buf);
218 if (rv < 0 || rv >= c->buf_size) {
219 /* buffer too small */
220 cddb_errno_log_crit(c, CDDB_ERR_LINE_SIZE);
221 free(buf);
222 return -1;
223 }
224 rv = sock_fwrite(buf, sizeof(char), rv, c);
225 free(buf);
226 return rv;
227 }
228
229 /* Time-out enabled work-alikes */
230
231 #ifdef HAVE_ALARM
232 /* time-out jump buffer */
233 static jmp_buf timeout_expired;
234
235 /* time-out signal handler */
alarm_handler(int signum)236 static void alarm_handler(int signum)
237 {
238 longjmp(timeout_expired, 1);
239 }
240 #endif
241
timeout_gethostbyname(const char * hostname,int timeout)242 struct hostent *timeout_gethostbyname(const char *hostname, int timeout)
243 {
244 #ifdef HAVE_ALARM
245 struct hostent *he = NULL;
246 struct sigaction action;
247 struct sigaction old;
248
249 /* no signal before setjmp */
250 alarm(0);
251
252 /* register signal handler */
253 memset(&action, 0, sizeof(action));
254 action.sa_handler = alarm_handler;
255 sigaction(SIGALRM, &action, &old);
256
257 /* save stack state */
258 if (!setjmp(timeout_expired)) {
259 alarm(timeout); /* set time-out alarm */
260 he = gethostbyname(hostname); /* execute DNS query */
261 alarm(0); /* reset alarm timer */
262 } else {
263 errno = ETIMEDOUT;
264 }
265 sigaction(SIGALRM, &old, NULL); /* restore previous signal handler */
266
267 return he;
268 #else
269 return gethostbyname(hostname); /* execute DNS query directly */
270 #endif
271 }
272
timeout_connect(int sockfd,const struct sockaddr * addr,size_t len,int timeout)273 int timeout_connect(int sockfd, const struct sockaddr *addr,
274 size_t len, int timeout)
275 {
276 int got_error = 0;
277
278 /* set socket to non-blocking */
279 #ifdef BEOS
280 int on = 1;
281
282 if (setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(on)) == -1) {
283 /* error while trying to set socket to non-blocking */
284 return -1;
285 }
286 #elif defined WIN32
287 unsigned long arg = 1;
288
289 if (ioctlsocket(sockfd, FIONBIO, &arg))
290 return -1;
291 #else
292 int flags;
293
294 flags = fcntl(sockfd, F_GETFL, 0);
295 flags |= O_NONBLOCK; /* add non-blocking flag */
296 if (fcntl(sockfd, F_SETFL, flags) == -1) {
297 return -1;
298 }
299 #endif /* BEOS */
300
301 /* try connecting */
302 if (connect(sockfd, addr, len) == -1) {
303 /* check whether we can continue */
304 if (errno == EINPROGRESS) {
305 int rv;
306 fd_set wfds;
307 struct timeval tv;
308 size_t l;
309
310 /* set up select time out */
311 tv.tv_sec = timeout;
312 tv.tv_usec = 0;
313
314 /* set up file descriptor set */
315 FD_ZERO(&wfds);
316 FD_SET(sockfd, &wfds);
317
318 /* wait for connect to finish */
319 rv = select(sockfd + 1, NULL, &wfds, NULL, &tv);
320 switch (rv) {
321 case 0: /* time out */
322 errno = ETIMEDOUT;
323 case -1: /* select error */
324 got_error = -1;
325 default:
326 /* we got connected, check error condition */
327 l = sizeof(rv);
328 getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &rv, &l);
329 if (rv) {
330 /* something went wrong, simulate normal connect behaviour */
331 errno = rv;
332 got_error = -1;
333 }
334 }
335 }
336 } else {
337 /* connect failed */
338 got_error = -1;
339 }
340 return got_error;
341 }
342