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