1 /* $Id: talktcp.c,v 1.12 2006/09/16 02:21:26 jared Exp $ */
2 #include "config.h"
3
4 extern struct clientstatus *clienthead;
5
6 /* handle sig pipes instead of killing process */
7 void
handle_sig_pipe()8 handle_sig_pipe()
9 {
10 }
11
is_open(int fd)12 int is_open(int fd)
13 /* check to see if the socket is ready for IO -- usually called once
14 open_host returns 10 (Connection in progress) It will return
15 if there is an error such as connection refused, conn timed out,
16 otherwise it returns 10 if the socket is not connected yet */
17 {
18 int vals = -1, size = 0;
19 unsigned int optval = 0;
20 int serrno = 0; /* saved errno */
21
22 size = sizeof(optval);
23
24 errno = 0;
25
26 /* check socket for error */
27 vals = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&optval, &size);
28
29 serrno = optval;
30
31 if (vals == -1)
32 {
33 errno = serrno;
34 if (debug)
35 perror("is_open:getsockopt");
36 }
37
38 if (serrno != 0)
39 switch(serrno)
40 {
41 case ECONNREFUSED:
42 case EINTR:
43 return SYSM_CONNREF;
44 case ENETUNREACH:
45 return SYSM_NETUNRCH;
46 case EHOSTDOWN:
47 case EHOSTUNREACH:
48 return SYSM_HOSTDOWN;
49 case ETIMEDOUT:
50 return SYSM_TIMEDOUT;
51 case EINPROGRESS:
52 return SYSM_INPROG;
53 case EBADF:
54 return -1;
55 default:
56 perror("is_open:getsockopt");
57 break;
58 }
59
60 /* check socket for connection */
61 vals = can_write(fd,0);
62 if (vals == 1)
63 return SYSM_OK;
64
65 if (debug)
66 print_err(0, "can_write returned %d", vals);
67
68 if (vals == -1)
69 {
70 print_err(0, "error checking socket for data");
71 return -1;
72 }
73
74 errno = 0;
75 /* check socket for error */
76 vals = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*) &optval, &size);
77 #ifdef __svr4__
78 serrno = errno;
79 #endif
80 #ifndef __svr4__
81 serrno = optval;
82 #endif
83 if (vals == -1)
84 perror("getsockopt");
85
86 if (vals == 0)
87 return -1;
88
89 if (serrno != 0)
90 switch(serrno)
91 {
92 case ECONNREFUSED:
93 case EINTR:
94 close(fd);
95 return SYSM_CONNREF;
96 case ENETUNREACH:
97 close(fd);
98 return SYSM_NETUNRCH;
99 case EHOSTDOWN:
100 case EHOSTUNREACH:
101 close(fd);
102 return SYSM_HOSTDOWN;
103 case ETIMEDOUT:
104 close(fd);
105 return SYSM_TIMEDOUT;
106 case EINPROGRESS:
107 return SYSM_INPROG;
108 case EBADF:
109 close(fd);
110 return -1;
111 default:
112 close(fd);
113 perror("getsockopt");
114 break;
115 }
116
117 /* return 0 if socket ok to do I/O */
118 vals = can_write(fd,0);
119
120 if (debug)
121 print_err(0, "talktcp.c:can_write returned %d", vals);
122
123 if ( vals == TRUE )
124 return SYSM_OK;
125
126 return SYSM_INPROG; /* connection still in progress */
127 }
128
129 /*
130 * sends the specified line out the specified filedescriptor -- we add
131 * cr and newline to the end. return: bytes written out socket
132 */
sendline(int fd,char * buffer)133 int sendline(int fd, char *buffer)
134 {
135 int val = -1;
136 char *space;
137
138 signal(SIGPIPE, handle_sig_pipe); /* set signal handler if there is
139 a problem */
140 space = MALLOC(strlen(buffer)+3, "sendline-temp buffer"); /* allocate memory for buffer */
141 memset(space, 0, strlen(buffer)+3);
142 strncpy(space, buffer, strlen(buffer)); /* copy stuff to temp buffer */
143 strncat(space, "\r\n", 2); /* add end stuff */
144 val = write(fd, space, strlen(space)/* - test - jared +1 */);
145 if (val == -1)
146 {
147 perror("sendline:write");
148 }
149 signal(SIGPIPE, SIG_DFL); /* set signal type back to default */
150 FREE(space); /* free the memory */
151 return val; /* return the number of bytes written */
152 }
153
can_write(int fd,int secs)154 int can_write(int fd, int secs)
155 {
156 /* check the descriptor fd for pending data -- wait up to 'secs'
157 seconds for data, else return a timeout (FALSE)
158 */
159 fd_set rd, wr, except;
160 struct timeval local_timeout;
161
162 int ret = 0;
163
164 /* set up all the data structures */
165 FD_ZERO(&wr);
166 FD_ZERO(&except);
167 FD_ZERO(&rd);
168 FD_SET(fd, &wr);
169 local_timeout.tv_sec = secs;
170 local_timeout.tv_usec = 0;
171 /* done */
172
173 ret = select(fd+1, &rd, &wr, &except, &local_timeout);
174 if (ret < 0)
175 {
176 if (debug)
177 {
178 perror("talktcp.c:can_write:select");
179 }
180 return -1;
181 }
182
183 if (ret == 0) /* if timelimit exceeded */
184 return FALSE;
185
186 /* else, there is data waiting in this socket */
187 return TRUE;
188 }
189
data_waiting_read(int fd,int secs)190 int data_waiting_read(int fd, int secs)
191 {
192 /* check the descriptor fd for pending data -- wait up to 'secs'
193 seconds for data, else return a timeout (FALSE)
194 */
195 fd_set rd, wr, except;
196 struct timeval local_timeout;
197
198 int ret = 0;
199
200 /* set up all the data structures */
201 FD_ZERO(&wr);
202 FD_ZERO(&except);
203 FD_ZERO(&rd);
204 FD_SET(fd, &rd);
205 local_timeout.tv_sec = secs;
206 local_timeout.tv_usec = 0;
207 /* done */
208
209 ret = select(fd+1, &rd, &wr, &except, &local_timeout);
210 if (ret < 0)
211 {
212 if (debug)
213 {
214 perror("talktcp.c:data_waiting_read:select");
215 }
216 return -1;
217 }
218
219 if (ret == 0) /* if timelimit exceeded */
220 return FALSE;
221
222 /* else, there is data waiting in this socket */
223 return TRUE;
224 }
225
getline_tcp(int fd,char * buffer)226 int getline_tcp(int fd, char *buffer)
227 {
228 char buf; /* buffer */
229 int red = 0; /* bytes read */
230 int counter = 0;
231 int selTimeout = 0;
232 int ret = 0;
233
234 /* Null it out */
235 memset(buffer, 0, sizeof(buffer));
236
237 while ( 1 ) /* while forever */
238 {
239 if (clienthead == NULL)
240 {
241 ret = data_waiting_read(fd, 1);
242 } else if (clienthead->filedes == -1)
243 {
244 ret = data_waiting_read(fd, 1);
245 } else {
246 ret = data_waiting_read(fd, 0);
247 }
248
249 if (ret == -1) /* if an error */
250 {
251 selTimeout++;
252 if (selTimeout > 20) /* if we've waited too long */
253 {
254 if (debug)
255 print_err(0, "Timeout REACHED-selTimeout");
256 return 0; /* bail */
257 }
258
259 if (clienthead->filedes == -1)
260 continue;
261
262 continue; /* go along and try again */
263 }
264
265 red = 0; /* init it */
266 if (ret > 0) /* if there's data waiting */
267 {
268 red = read(fd, &buf, 1);
269 }
270
271 if (red == -1)
272 {
273 perror("talktcp.c:getline_tcp:read");
274 return -1;
275 }
276
277 if (red == 0)
278 {
279 counter++;
280 if (counter > 20)
281 {
282 return -1; /* error */
283 }
284 continue;
285 }
286 if ((strlen(buffer) == 0) && ((buf == '\n') || (buf == '\r')))
287 {
288 continue; /* throw \r or \n if at beginning of line */
289 }
290 if ((buf == '\n') || (buf == '\r'))
291 {
292 return 0;
293 }
294 strncat(buffer, &buf, 1);
295 if (strlen(buffer) > 200)
296 {
297 return 0;
298 }
299 }
300 }
301
302 /* this socket will be used for both sending and receiving */
open_host(char * host,int port,int * filedes,int ltimeout)303 int open_host(char *host, int port, int *filedes, int ltimeout)
304 {
305 int fails = 0;
306 struct sockaddr_in name;
307 struct my_hostent *hp;
308 int errcode = -1;
309 int serrno = 0 ;
310 time_t start_time;
311
312 /* record current time */
313 start_time = time(NULL);
314
315 /* open a filedescriptor to use */
316 *filedes = open_sock();
317
318 /* check for error */
319 if (*filedes == -1)
320 {
321 /* try again.. maybe a fluke */
322 *filedes = open_sock();
323 if (*filedes == -1)
324 return(-1); /* return error */
325 }
326
327 /* get the data structure for the hostname */
328 hp = my_gethostbyname(host, AF_INET);
329
330 if (hp == 0)
331 {
332 /* print an error if one happened */
333 if (debug)
334 print_err(0, "%s: unknown host", host);
335 return SYSM_NODNS;
336 }
337
338 /* zero out the space */
339 memset ( &name, 0, sizeof ( name ) );
340
341 /* copy data */
342 memcpy((char*)&name.sin_addr, (char*)hp->my_h_addr_v4, hp->h_length_v4);
343
344 /* set family type */
345 name.sin_family = AF_INET;
346
347 /* set the port we're connecting to */
348 name.sin_port = htons(port);
349
350 /* try to make the connection */
351 while (1)
352 {
353 if (debug)
354 print_err(0, "Calling connect() with fd = %d", *filedes);
355
356 errcode = connect(*filedes, (struct sockaddr*)&name,
357 sizeof(struct sockaddr_in));
358 /* doesn't happen often, but can, so we must trap
359 * for it, duh! PR#34
360 */
361
362 if (errcode == 0)
363 return SYSM_OK;
364
365 /* Save that error code so it doesn't get clobbered */
366 serrno = errno;
367
368 if (debug)
369 print_err(0, "serrno = %d, errcode = %d", serrno, errcode);
370
371 if ((errcode == -1) && (serrno == EINPROGRESS))
372 {
373 if (debug)
374 {
375 print_err(0, "talktcp.c:connection in progress");
376 }
377 break;
378 } else if (errcode == -1) {
379 perror("open_host:connect");
380 }
381
382 if ((errcode == -1) && (serrno != EINPROGRESS))
383 {
384 if (close(*filedes) == -1)
385 {
386 return -1; /* close the fd incase it's left */
387 }
388 fails ++; /* failure my son */
389
390 switch(serrno)
391 {
392 case ECONNREFUSED:
393 case EINTR:
394 if (fails >= MAX_TRIES)
395 return SYSM_CONNREF;
396 *filedes = open_sock();
397 break;
398 case ENETUNREACH:
399 return SYSM_NETUNRCH;
400 case EHOSTDOWN:
401 case EHOSTUNREACH:
402 return SYSM_HOSTDOWN;
403 case ETIMEDOUT:
404 return SYSM_TIMEDOUT;
405 default:
406 break;
407 }
408 }
409 }
410
411 if (serrno == EINPROGRESS)
412 while (1)
413 {
414 errcode = is_open(*filedes);
415 if (errcode == 0)
416 {
417 break;
418 }
419
420 /* check to see if we should have timed out already */
421 if (time(NULL) >= start_time+ltimeout)
422 {
423 if (debug)
424 print_err(0, "open_host: timeout reached");
425 if (close(*filedes) == -1)
426 return -1; /* don't leak that fd */
427 return SYSM_TIMEDOUT; /* return connection timed out */
428 }
429
430 if (debug)
431 print_err(0, "While connecting, got %s(%d)",
432 errtostr(errcode),errcode);
433 if (errcode == 1 || errcode == 2 || errcode == 3 ||
434 errcode == 4)
435 {
436 if (close(*filedes) == -1)
437 {
438 perror("talktcp.c:open_host:second_loop_close");
439 return -1;
440 }
441 return errcode;
442 }
443 }
444
445 return 0; /* return ok status */
446 }
447
448 /*
449 * Create a nonblocking socket for the other functions to
450 * handle and do processing with
451 */
open_sock()452 int open_sock()
453 {
454 int sock = 0;
455
456 sock = socket(AF_INET, SOCK_STREAM, 0);
457
458 if (sock == -1)
459 {
460 perror("opening datagram socket");
461 if (debug)
462 print_err(0, "sock = %d at return(-1)", sock);
463 return -1;
464 }
465
466 set_nonblock(sock);
467
468 return sock;
469 }
470
471 /*
472 *
473 */
set_nonblock(int sock)474 void set_nonblock(int sock)
475 {
476 int errcode;
477
478 #if defined (NBIO_FCNTL)
479 #if ! defined (FNDELAY)
480 #define FNDELAY O_NDELAY
481 #endif /* ! defined (FNDELAY) */
482
483 errcode = fcntl (sock, F_SETFL, FNDELAY) ;
484
485 #else /* defined NBIO_FCNTL */
486 /* linux and whatnot */
487 int state = 1;
488
489 errcode = ioctl (sock, FIONBIO, (char *) &state) ;
490 #endif /* defined NBIO_FCNTL */
491
492 if (errcode == -1)
493 {
494 perror("set_nonblock");
495 }
496
497 }
498
499 /*
500 * wait until we are ready
501 */
blocktillready(int fd,int secs)502 void blocktillready(int fd, int secs)
503 {
504 time_t start = time(NULL); /* duh */
505
506 while ((time(NULL) - start) > secs)
507 {
508 if (is_open(fd))
509 return;
510 sleep(1);
511 }
512 }
513