1 /*+++++++++++++++++
2   connect.c - read/write replacements with timeouts
3   markus@mhoenicka.de 2-10-00
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
20 
21 
22 /* Parts of this file are inspired considerably by a similar file in
23    Wget (Copyright (C) 1995, 1996, 1997 Free Software Foundation,
24    Inc.) */
25 
26 
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32 
33 #include <errno.h>
34 #include <string.h>
35 
36 #include "connect.h"
37 #include "refdb.h"
38 #include "refstat.h"
39 
40 extern int n_refdb_timeout; /* timeout in seconds for read/write on sockets */
41 extern int n_abort_connect; /* if 1, user tries to abort current connection */
42 
43 const char cs_term[5] = {'\0', '\0', '\0', '\0', '\0'}; /* client-server message terminator */
44 
45 /* forward declarations of local functions */
46 static int select_fd (int fd, int maxtime, int writep);
47 static int get_numstatus(const char* status);
48 
49 
50 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
51   select_fd(): Wait for file descriptor FD to be readable, MAXTIME
52   being the timeout in seconds.  If WRITEP is non-zero, checks for FD
53   being writable instead.
54 
55   static int select_fd returns 1 if FD is accessible, 0 for timeout
56                        and -1 for error in select().
57 
58   int fd the file descriptor of a socket
59 
60   int maxtime the time in seconds that the function will wait before
61       a timeout occurs
62 
63   int writep if 1, checks for fd being writable. if 0, checks for fd
64       being readable
65 
66   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
select_fd(int fd,int maxtime,int writep)67 static int select_fd (int fd, int maxtime, int writep)
68 {
69   fd_set fds, exceptfds;
70   struct timeval timeout;
71 
72   FD_ZERO (&fds);
73   FD_SET (fd, &fds);
74   FD_ZERO (&exceptfds);
75   FD_SET (fd, &exceptfds);
76   timeout.tv_sec = maxtime;
77   timeout.tv_usec = 0;
78   /* HPUX reportedly warns here.  What is the correct incantation?  */
79   return select (fd + 1, writep ? NULL : &fds, writep ? &fds : NULL,
80 		 &exceptfds, &timeout);
81 }
82 
83 
84 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
85   iread(): Read at most LEN bytes from FD, storing them to BUF.  This
86   is virtually the same as read(), but takes care of EINTR braindamage
87   and uses select() to timeout the stale connections (a connection is
88   stale if more than n_timeout time is spent in select() or read()).
89 
90   int iread the number of bytes read from fd, or -1 if timeout
91 
92   char *buf a pointer to a character buffer which receives the data
93 
94   int len the number of bytes that iread will attempt to read from fd
95 
96   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
iread(int fd,char * buf,int len)97 int iread (int fd, char *buf, int len) {
98   int res;
99   int n_byte_read = 0;
100 
101   while (len > 0) { /* read until we have all or until timeout occurs */
102     do {
103       if (n_refdb_timeout) {
104 	do {
105 	  res = select_fd (fd, n_refdb_timeout, 0);
106 	} while (res == -1 && errno == EINTR && !n_abort_connect);
107 	if (n_abort_connect) {
108 	  n_abort_connect = 0;
109 	  return -1;
110 	}
111 	if (res <= 0) {
112 	  /* Set errno to ETIMEDOUT on timeout.  */
113 	  if (res == 0)
114 	    /* #### Potentially evil!  */
115 	    errno = ETIMEDOUT;
116 	  return -1;
117 	}
118       }
119       res = read(fd, buf, len); /* try to read data */
120     } while (res == -1 && errno == EINTR);
121     if (res <= 0)
122       break;
123     n_byte_read += res;
124     buf += res;
125     len -= res;
126   }
127 /*    printf("%d\n", n_byte_read); */
128   return n_byte_read;
129 }
130 
131 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
132   tread(): Read at most LEN bytes from FD, storing them to BUF.  This
133   is virtually the same as iread(), but it checks after each success-
134   ful read() whether a string is complete (i.e. whether the
135   terminating sequence was received). In this case, the function
136   returns immediately, instead of timing out, even if less byte than
137   requested were received.
138 
139   int tread the number of bytes read from fd, or -1 if timeout
140 
141   char *buf a pointer to a character buffer which receives the data
142 
143   int len the number of bytes that iread will attempt to read from fd
144 
145   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
tread(int fd,char * buf,int len)146 int tread (int fd, char *buf, int len) {
147   int res;
148   int n_byte_read = 0;
149   char* buf_start;
150 
151   buf_start = buf;
152 
153   while (len > 0) { /* read until we have all, a complete string, or timeout */
154     do {
155       if (n_refdb_timeout) {
156 	do {
157 	  res = select_fd (fd, n_refdb_timeout, 0);
158 	} while (res == -1 && errno == EINTR && !n_abort_connect);
159 	if (n_abort_connect) {
160 	  n_abort_connect = 0;
161 	  return -1;
162 	}
163 	if (res <= 0) {
164 	  /* Set errno to ETIMEDOUT on timeout.  */
165 	  if (res == 0)
166 	    /* #### Potentially evil!  */
167 	    errno = ETIMEDOUT;
168 	  return -1;
169 	}
170       }
171       res = read(fd, buf, len); /* read some data */
172       if (res > 0) { /* see whether we've got a complete string */
173 	if (n_byte_read+res>= TERM_LEN
174 	    && !memcmp((const void*)(buf_start+n_byte_read+res-TERM_LEN),
175 		       (const void*)cs_term,
176 		       TERM_LEN)) {
177 /* 	if (buf[res-1] == '\0') { */ /* complete string received */
178 	  n_byte_read += res;
179 	  return n_byte_read; /* get back w/o timeout */
180 	}
181       }
182     } while (res == -1 && errno == EINTR);
183     if (res <= 0)
184       break;
185     n_byte_read += res;
186     buf += res;
187     len -= res;
188   }
189 /*    printf("%d\n", n_byte_read); */
190   return n_byte_read;
191 }
192 
193 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
194   iwrite(): Write LEN bytes from BUF to FD.  This is similar to
195   iread().  It makes sure that all of BUF is actually
196   written to FD, so callers needn't bother with checking that the
197   return value equals to LEN.  Instead, you should simply check
198   for -1.
199 
200   int iwrite the number of bytes actually written to fd, or -1 if
201              timeout
202 
203   char *buf a pointer to a character buffer which holds the data
204 
205   int len the number of bytes that iwrite will attempt to write to fd
206 
207   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
iwrite(int fd,const char * buf,int len)208 int iwrite (int fd, const char *buf, int len) {
209   int res = 0;
210   int n_byte_written = 0;
211 
212   /* `write' may write less than LEN bytes, thus the outward loop
213      keeps trying it until all was written, or an error occurred.  The
214      inner loop is reserved for the usual EINTR f*kage, and the
215      innermost loop deals with the same during select().  */
216   while (len > 0) {
217     do {
218       if (n_refdb_timeout) {
219 	do {
220 	  res = select_fd (fd, n_refdb_timeout, 1);
221 	} while (res == -1 && errno == EINTR && !n_abort_connect);
222 	if (n_abort_connect) {
223 	  n_abort_connect = 0;
224 	  return -1;
225 	}
226 	if (res <= 0) {
227 	  /* Set errno to ETIMEDOUT on timeout.  */
228 	  if (res == 0)
229 	    /* #### Potentially evil!  */
230 	    errno = ETIMEDOUT;
231 	  return -1;
232 	}
233       }
234       res = write (fd, buf, len); /* write some data */
235       n_byte_written += res;
236     } while (res == -1 && errno == EINTR);
237     if (res <= 0)
238       break;
239     buf += res;
240     len -= res;
241   }
242 /*    printf("%d\n", n_byte_written); */
243   return n_byte_written;
244 }
245 
246 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
247   tiwrite(): Write bytes from BUF to FD and terminate if requested.
248              This is similar to iwrite(). However, it determines the
249 	     number of bytes to write from the passed string and adds
250 	     the message terminator automatically if requested.
251 
252   int tiwrite the number of bytes actually written to fd, or -1 if
253              timeout
254 
255   char *buf a pointer to a character buffer which holds the data
256 
257   int n_term if 1, send a terminator sequence after sending the buffer
258 
259   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
tiwrite(int fd,const char * buf,int n_term)260 int tiwrite (int fd, const char *buf, int n_term) {
261   size_t buf_len;
262   int n_byte_written;
263   int n_byte_written_total = 0;
264 
265   buf_len = strlen(buf);
266 
267   if (buf_len) {
268     /* send buffer proper */
269     n_byte_written = iwrite(fd, buf, buf_len);
270 
271     if (n_byte_written == -1) {
272       return -1;
273     }
274     else {
275       n_byte_written_total = n_byte_written;
276     }
277   }
278 
279   if (n_term) {
280     /* send terminator */
281     n_byte_written = iwrite(fd, cs_term, TERM_LEN);
282 
283     if (n_byte_written == -1) {
284       return -1;
285     }
286     else {
287       n_byte_written_total += n_byte_written;
288     }
289   }
290 
291   return n_byte_written_total;
292 }
293 
294 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
295   get_trailz(): counts the trailing \0 in a buffer
296 
297   int get_trailz the number of trailing \0, or -1 if buf is NULL
298 
299   char *buf a pointer to a buffer
300 
301   int numbyte offset at which the function should check the buffer
302                 (going from offset towards the start of the buffer)
303 
304   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
get_trailz(const char * buf,int numbyte)305 int get_trailz(const char* buf, int numbyte) {
306   int numz = 0;
307   int i = numbyte-1;
308 
309   if (!buf) {
310     return -1;
311   }
312 
313   while (!buf[i] && i>=0) {
314     numz++;
315     i--;
316   }
317   return numz;
318 }
319 
320 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
321   send_status(): sends the status bytes that precede each message
322 
323   int send_status returns 0 if ok, 1 if error
324 
325   int fd file descriptor
326 
327   int n_status status code
328 
329   int n_term send terminator if 1
330 
331   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
send_status(int fd,int n_status,int n_term)332 int send_status(int fd, int n_status, int n_term) {
333   int n_byte_written;
334 
335 /*   printf("sending status %d<< term:%d\n", n_status, n_term); */
336 
337   if (iwrite(fd, get_status_string(n_status), STATUS_LEN) == -1) {
338 /*       printf("error sending status %d<< term:%d\n", n_status, n_term); */
339     return 1;
340   }
341 
342   if (n_term) {
343     /* send terminator */
344     n_byte_written = iwrite(fd, cs_term, TERM_LEN);
345 
346     if (n_byte_written == -1) {
347 /*       printf("error sending status %d<< term:%d\n", n_status, n_term); */
348       return 1;
349     }
350   }
351 
352 /*   printf("done sending status %d<< term:%d\n", n_status, n_term); */
353   return 0;
354 }
355 
356 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
357   read_status(): reads the status bytes that precede each message
358 
359   int read_status returns 0 if ok, 1 if error
360 
361   int fd file descriptor
362 
363   const char* status ptr to the status message
364 
365   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
read_status(int fd)366 int read_status(int fd) {
367   char status[STATUS_LEN+1] = {'\0', '\0', '\0', '\0'};
368 
369 /*   printf("receiving status...\n"); */
370   if (iread(fd, status, STATUS_LEN) != STATUS_LEN) {
371 /*     printf("read error, received status %s<<\n", status); */
372     return 1;
373   }
374 
375   status[3] = '\0';
376 
377 /*   printf("received status %s<<\n", status); */
378   return get_numstatus(status);
379 }
380 
381 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
382   get_numstatus(): retrieves the numeric status of a status string
383 
384   int get_numstatus returns status number if ok, -1 if error
385 
386   const char* status ptr to the status string
387 
388   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
get_numstatus(const char * status)389 static int get_numstatus(const char* status) {
390   int i;
391 
392   for (i=0; refstat[i].n_status != 999; i++) {
393     if (!strncmp(refstat[i].status, status, STATUS_LEN)) {
394       return refstat[i].n_status;
395     }
396   }
397 
398   /* unknown status message - should never happen */
399   return 1; /* unspecified error */
400 }
401 
402 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
403   get_status_string(): retrieves the status string of a given status number
404 
405   const char* get_status_string returns status string if ok, "999" if error
406 
407   const char* status ptr to the status string
408 
409   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
get_status_string(int n_status)410 const char* get_status_string(int n_status) {
411   int i;
412 
413   for (i=0; refstat[i].n_status != 999; i++) {
414     if (refstat[i].n_status == n_status) {
415       return refstat[i].status;
416     }
417   }
418 
419   /* unknown status message - should never happen */
420   return "999";
421 }
422 
423 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
424   get_status_msg(): retrieves the status message of a given status number
425 
426   const char* get_status_msg returns status message if ok,
427                              empty string if error
428 
429   int n_status status number
430 
431   ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
get_status_msg(int n_status)432 const char* get_status_msg(int n_status) {
433   int i;
434 
435   for (i=0; refstat[i].n_status != 999; i++) {
436     if (refstat[i].n_status == n_status) {
437       return refstat[i].msg;
438     }
439   }
440 
441   /* unknown status message - should never happen */
442   return "undefined error";
443 }
444 
445