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