]>
Commit | Line | Data |
---|---|---|
18038256 | 1 | /* GNU Thales - IRC to Relational Database Gateway |
2ace9480 | 2 | * Copyright (C) 2002 Lucas Nussbaum <lucas@lucas-nussbaum.net> |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
17 | */ | |
18 | ||
19 | #include "thales.h" | |
20 | #include "log.h" | |
21 | ||
22 | extern int debug; | |
23 | ||
24 | /*************************************************************************/ | |
25 | /*************************************************************************/ | |
26 | ||
27 | /* Read from a socket with buffering. */ | |
28 | ||
29 | static char read_netbuf[NET_BUFSIZE]; | |
30 | static char *read_curpos = read_netbuf; /* Next byte to return */ | |
31 | static char *read_bufend = read_netbuf; /* Next position for data from socket */ | |
32 | static char *const read_buftop = read_netbuf + NET_BUFSIZE; | |
33 | int total_read = 0; | |
34 | ||
35 | ||
36 | /* Return amount of data in read buffer. */ | |
37 | ||
38 | int read_buffer_len() | |
39 | { | |
40 | if (read_bufend >= read_curpos) | |
41 | return read_bufend - read_curpos; | |
42 | else | |
43 | return (read_bufend + NET_BUFSIZE) - read_curpos; | |
44 | } | |
45 | ||
46 | ||
47 | /* Read data. */ | |
48 | ||
49 | static int buffered_read(int fd, char *buf, int len) | |
50 | { | |
51 | int nread, left = len; | |
52 | fd_set fds; | |
53 | struct timeval tv = { 0, 0 }; | |
54 | int errno_save = errno; | |
55 | ||
56 | if (fd < 0) | |
57 | { | |
58 | errno = EBADF; | |
59 | return -1; | |
60 | } | |
61 | while (left > 0) | |
62 | { | |
63 | struct timeval *tvptr = (read_bufend == read_curpos ? NULL : &tv); | |
64 | FD_ZERO(&fds); | |
65 | FD_SET(fd, &fds); | |
66 | while (read_bufend != read_curpos - 1 | |
67 | && !(read_curpos == read_netbuf | |
68 | && read_bufend == read_buftop - 1) | |
69 | && select(fd + 1, &fds, 0, 0, tvptr) == 1) | |
70 | { | |
71 | int maxread; | |
72 | tvptr = &tv; /* don't wait next time */ | |
73 | if (read_bufend < read_curpos) /* wrapped around? */ | |
74 | maxread = (read_curpos - 1) - read_bufend; | |
75 | else if (read_curpos == read_netbuf) | |
76 | maxread = read_buftop - read_bufend - 1; | |
77 | else | |
78 | maxread = read_buftop - read_bufend; | |
79 | nread = read(fd, read_bufend, maxread); | |
80 | errno_save = errno; | |
81 | if (debug >= 3) | |
18038256 | 82 | mylog("debug: buffered_read wanted %d, got %d", maxread, nread); |
2ace9480 | 83 | if (nread <= 0) |
84 | break; | |
85 | read_bufend += nread; | |
86 | if (read_bufend == read_buftop) | |
87 | read_bufend = read_netbuf; | |
88 | } | |
89 | if (read_curpos == read_bufend) /* No more data on socket */ | |
90 | break; | |
91 | /* See if we can gobble up the rest of the buffer. */ | |
92 | if (read_curpos + left >= read_buftop && read_bufend < read_curpos) | |
93 | { | |
94 | nread = read_buftop - read_curpos; | |
95 | memcpy(buf, read_curpos, nread); | |
96 | buf += nread; | |
97 | left -= nread; | |
98 | read_curpos = read_netbuf; | |
99 | } | |
100 | /* Now everything we need is in a single chunk at read_curpos. */ | |
101 | if (read_bufend > read_curpos && read_bufend - read_curpos < left) | |
102 | nread = read_bufend - read_curpos; | |
103 | else | |
104 | nread = left; | |
105 | if (nread) | |
106 | { | |
107 | memcpy(buf, read_curpos, nread); | |
108 | buf += nread; | |
109 | left -= nread; | |
110 | read_curpos += nread; | |
111 | } | |
112 | } | |
113 | total_read += len - left; | |
114 | if (debug >= 4) | |
115 | { | |
18038256 | 116 | mylog("debug: buffered_read(%d,%p,%d) returning %d", |
2ace9480 | 117 | fd, buf, len, len - left); |
118 | } | |
119 | errno = errno_save; | |
120 | return len - left; | |
121 | } | |
122 | ||
123 | /* Optimized version of the above for reading a single character; returns | |
124 | * the character in an int or EOF, like fgetc(). */ | |
125 | ||
126 | static int buffered_read_one(int fd) | |
127 | { | |
128 | int nread; | |
129 | fd_set fds; | |
130 | struct timeval tv = { 0, 0 }; | |
131 | char c; | |
132 | struct timeval *tvptr = (read_bufend == read_curpos ? NULL : &tv); | |
133 | int errno_save = errno; | |
134 | ||
135 | if (fd < 0) | |
136 | { | |
137 | errno = EBADF; | |
138 | return -1; | |
139 | } | |
140 | FD_ZERO(&fds); | |
141 | FD_SET(fd, &fds); | |
142 | while (read_bufend != read_curpos - 1 | |
143 | && !(read_curpos == read_netbuf | |
144 | && read_bufend == read_buftop - 1) | |
145 | && select(fd + 1, &fds, 0, 0, tvptr) == 1) | |
146 | { | |
147 | int maxread; | |
148 | tvptr = &tv; /* don't wait next time */ | |
149 | if (read_bufend < read_curpos) /* wrapped around? */ | |
150 | maxread = (read_curpos - 1) - read_bufend; | |
151 | else if (read_curpos == read_netbuf) | |
152 | maxread = read_buftop - read_bufend - 1; | |
153 | else | |
154 | maxread = read_buftop - read_bufend; | |
155 | nread = read(fd, read_bufend, maxread); | |
156 | errno_save = errno; | |
157 | if (debug >= 3) | |
18038256 | 158 | mylog("debug: buffered_read_one wanted %d, got %d", maxread, nread); |
2ace9480 | 159 | if (nread <= 0) |
160 | break; | |
161 | read_bufend += nread; | |
162 | if (read_bufend == read_buftop) | |
163 | read_bufend = read_netbuf; | |
164 | } | |
165 | if (read_curpos == read_bufend) | |
166 | { /* No more data on socket */ | |
167 | if (debug >= 4) | |
18038256 | 168 | mylog("debug: buffered_read_one(%d) returning %d", fd, EOF); |
2ace9480 | 169 | errno = errno_save; |
170 | return EOF; | |
171 | } | |
172 | c = *read_curpos++; | |
173 | if (read_curpos == read_buftop) | |
174 | read_curpos = read_netbuf; | |
175 | total_read++; | |
176 | if (debug >= 4) | |
18038256 | 177 | mylog("debug: buffered_read_one(%d) returning %d", fd, c); |
2ace9480 | 178 | return (int) c & 0xFF; |
179 | } | |
180 | ||
181 | /*************************************************************************/ | |
182 | ||
183 | /* Write to a socket with buffering. Note that this assumes only one | |
184 | * socket. */ | |
185 | ||
186 | static char write_netbuf[NET_BUFSIZE]; | |
187 | static char *write_curpos = write_netbuf; /* Next byte to write to socket */ | |
188 | static char *write_bufend = write_netbuf; /* Next position for data to socket */ | |
189 | static char *const write_buftop = write_netbuf + NET_BUFSIZE; | |
190 | static int write_fd = -1; | |
191 | int total_written; | |
192 | ||
193 | ||
194 | /* Return amount of data in write buffer. */ | |
195 | ||
196 | int write_buffer_len() | |
197 | { | |
198 | if (write_bufend >= write_curpos) | |
199 | return write_bufend - write_curpos; | |
200 | else | |
201 | return (write_bufend + NET_BUFSIZE) - write_curpos; | |
202 | } | |
203 | ||
204 | ||
205 | /* Helper routine to try and write up to one chunk of data from the buffer | |
206 | * to the socket. Return how much was written. */ | |
207 | ||
208 | static int flush_write_buffer(int wait) | |
209 | { | |
210 | fd_set fds; | |
211 | struct timeval tv = { 0, 0 }; | |
212 | int errno_save = errno; | |
213 | ||
214 | if (write_bufend == write_curpos || write_fd == -1) | |
215 | return 0; | |
216 | FD_ZERO(&fds); | |
217 | FD_SET(write_fd, &fds); | |
218 | if (select(write_fd + 1, 0, &fds, 0, wait ? NULL : &tv) == 1) | |
219 | { | |
220 | int maxwrite, nwritten; | |
221 | if (write_curpos > write_bufend) /* wrapped around? */ | |
222 | maxwrite = write_buftop - write_curpos; | |
223 | else if (write_bufend == write_netbuf) | |
224 | maxwrite = write_buftop - write_curpos - 1; | |
225 | else | |
226 | maxwrite = write_bufend - write_curpos; | |
227 | nwritten = write(write_fd, write_curpos, maxwrite); | |
228 | errno_save = errno; | |
229 | if (debug >= 3) | |
18038256 | 230 | mylog("debug: flush_write_buffer wanted %d, got %d", maxwrite, |
2ace9480 | 231 | nwritten); |
232 | if (nwritten > 0) | |
233 | { | |
234 | write_curpos += nwritten; | |
235 | if (write_curpos == write_buftop) | |
236 | write_curpos = write_netbuf; | |
237 | total_written += nwritten; | |
238 | return nwritten; | |
239 | } | |
240 | } | |
241 | errno = errno_save; | |
242 | return 0; | |
243 | } | |
244 | ||
245 | ||
246 | /* Write data. */ | |
247 | ||
248 | static int buffered_write(int fd, char *buf, int len) | |
249 | { | |
250 | int nwritten, left = len; | |
251 | int errno_save = errno; | |
252 | ||
253 | if (fd < 0) | |
254 | { | |
255 | errno = EBADF; | |
256 | return -1; | |
257 | } | |
258 | write_fd = fd; | |
259 | ||
260 | while (left > 0) | |
261 | { | |
262 | ||
263 | /* Don't try putting anything in the buffer if it's full. */ | |
264 | if (write_curpos != write_bufend + 1 && | |
265 | (write_curpos != write_netbuf | |
266 | || write_bufend != write_buftop - 1)) | |
267 | { | |
268 | /* See if we need to write up to the end of the buffer. */ | |
269 | if (write_bufend + left >= write_buftop | |
270 | && write_curpos <= write_bufend) | |
271 | { | |
272 | nwritten = write_buftop - write_bufend; | |
273 | memcpy(write_bufend, buf, nwritten); | |
274 | buf += nwritten; | |
275 | left -= nwritten; | |
276 | write_bufend = write_netbuf; | |
277 | } | |
278 | /* Now we can copy a single chunk to write_bufend. */ | |
279 | if (write_curpos > write_bufend | |
280 | && write_curpos - write_bufend - 1 < left) | |
281 | nwritten = write_curpos - write_bufend - 1; | |
282 | else | |
283 | nwritten = left; | |
284 | if (nwritten) | |
285 | { | |
286 | memcpy(write_bufend, buf, nwritten); | |
287 | buf += nwritten; | |
288 | left -= nwritten; | |
289 | write_bufend += nwritten; | |
290 | } | |
291 | } | |
292 | ||
293 | /* Now write to the socket as much as we can. */ | |
294 | if (write_curpos == write_bufend + 1 || | |
295 | (write_curpos == write_netbuf | |
296 | && write_bufend == write_buftop - 1)) | |
297 | flush_write_buffer(1); | |
298 | else | |
299 | flush_write_buffer(0); | |
300 | errno_save = errno; | |
301 | if (write_curpos == write_bufend + 1 || | |
302 | (write_curpos == write_netbuf | |
303 | && write_bufend == write_buftop - 1)) | |
304 | { | |
305 | /* Write failed on full buffer */ | |
306 | break; | |
307 | } | |
308 | } | |
309 | ||
310 | if (debug >= 4) | |
311 | { | |
18038256 | 312 | mylog("debug: buffered_write(%d,%p,%d) returning %d", |
2ace9480 | 313 | fd, buf, len, len - left); |
314 | } | |
315 | errno = errno_save; | |
316 | return len - left; | |
317 | } | |
318 | ||
319 | /* Optimized version of the above for writing a single character; returns | |
320 | * the character in an int or EOF, like fputc(). Commented out because it | |
321 | * isn't currently used. */ | |
322 | ||
323 | #if 0 | |
324 | static int buffered_write_one(int c, int fd) | |
325 | { | |
326 | struct timeval tv = { 0, 0 }; | |
327 | ||
328 | if (fd < 0) | |
329 | { | |
330 | errno = EBADF; | |
331 | return -1; | |
332 | } | |
333 | write_fd = fd; | |
334 | ||
335 | /* Try to flush the buffer if it's full. */ | |
336 | if (write_curpos == write_bufend + 1 || | |
337 | (write_curpos == write_netbuf && write_bufend == write_buftop - 1)) | |
338 | { | |
339 | flush_write_buffer(1); | |
340 | if (write_curpos == write_bufend + 1 || | |
341 | (write_curpos == write_netbuf | |
342 | && write_bufend == write_buftop - 1)) | |
343 | { | |
344 | /* Write failed */ | |
345 | if (debug >= 4) | |
18038256 | 346 | mylog("debug: buffered_write_one(%d) returning %d", fd, EOF); |
2ace9480 | 347 | return EOF; |
348 | } | |
349 | } | |
350 | ||
351 | /* Write the character. */ | |
352 | *write_bufend++ = c; | |
353 | if (write_bufend == write_buftop) | |
354 | write_bufend = write_netbuf; | |
355 | ||
356 | /* Move it to the socket if we can. */ | |
357 | flush_write_buffer(0); | |
358 | ||
359 | if (debug >= 4) | |
18038256 | 360 | mylog("debug: buffered_write_one(%d) returning %d", fd, c); |
2ace9480 | 361 | return (int) c & 0xFF; |
362 | } | |
363 | #endif /* 0 */ | |
364 | ||
365 | /*************************************************************************/ | |
366 | /*************************************************************************/ | |
367 | ||
368 | static int lastchar = EOF; | |
369 | ||
370 | int sgetc(int s) | |
371 | { | |
372 | int c; | |
373 | ||
374 | if (lastchar != EOF) | |
375 | { | |
376 | c = lastchar; | |
377 | lastchar = EOF; | |
378 | return c; | |
379 | } | |
380 | return buffered_read_one(s); | |
381 | } | |
382 | ||
383 | int sungetc(int c, int s) | |
384 | { | |
385 | return lastchar = c; | |
386 | } | |
387 | ||
388 | /*************************************************************************/ | |
389 | ||
390 | /* If connection was broken, return NULL. If the read timed out, return | |
391 | * (char *)-1. | |
392 | */ | |
393 | ||
394 | char *sgets(char *buf, int len, int s) | |
395 | { | |
396 | int c = 0; | |
397 | struct timeval tv; | |
398 | fd_set fds; | |
399 | char *ptr = buf; | |
400 | ||
401 | if (len == 0) | |
402 | return NULL; | |
403 | FD_SET(s, &fds); | |
404 | tv.tv_sec = 5; | |
405 | tv.tv_usec = 0; | |
406 | while (read_buffer_len() == 0 && | |
407 | (c = select(s + 1, &fds, NULL, NULL, &tv)) < 0) | |
408 | { | |
409 | if (errno != EINTR) | |
410 | break; | |
411 | } | |
412 | if (read_buffer_len() == 0 && c == 0) | |
413 | return (char *) -1; | |
414 | c = sgetc(s); | |
415 | while (--len && (*ptr++ = c) != '\n' && (c = sgetc(s)) >= 0) | |
416 | ; | |
417 | if (c < 0) | |
418 | return NULL; | |
419 | *ptr = 0; | |
420 | return buf; | |
421 | } | |
422 | ||
423 | /*************************************************************************/ | |
424 | ||
425 | /* sgets2: Read a line of text from a socket, and strip newline and | |
426 | * carriage return characters from the end of the line. | |
427 | */ | |
428 | ||
429 | char *sgets2(char *buf, int len, int s) | |
430 | { | |
431 | char *str = sgets(buf, len, s); | |
432 | ||
433 | if (!str || str == (char *) -1) | |
434 | return str; | |
435 | str = buf + strlen(buf) - 1; | |
436 | if (*str == '\n') | |
437 | *str-- = 0; | |
438 | if (*str == '\r') | |
439 | *str = 0; | |
440 | return buf; | |
441 | } | |
442 | ||
443 | /*************************************************************************/ | |
444 | ||
445 | /* Read from a socket. (Use this instead of read() because it has | |
446 | * buffering.) */ | |
447 | ||
448 | int sread(int s, char *buf, int len) | |
449 | { | |
450 | return buffered_read(s, buf, len); | |
451 | } | |
452 | ||
453 | /*************************************************************************/ | |
454 | ||
455 | int sputs(char *str, int s) | |
456 | { | |
457 | return buffered_write(s, str, strlen(str)); | |
458 | } | |
459 | ||
460 | /*************************************************************************/ | |
461 | ||
462 | int sockprintf(int s, char *fmt, ...) | |
463 | { | |
464 | va_list args; | |
465 | char buf[16384]; /* Really huge, to try and avoid truncation */ | |
466 | ||
467 | va_start(args, fmt); | |
468 | return buffered_write(s, buf, vsnprintf(buf, sizeof(buf), fmt, args)); | |
469 | } | |
470 | ||
471 | ||
472 | /*************************************************************************/ | |
473 | ||
474 | /* lhost/lport specify the local side of the connection. If they are not | |
475 | * given (lhost==NULL, lport==0), then they are left free to vary. | |
476 | */ | |
477 | ||
478 | int conn(const char *host, int port, const char *lhost, int lport) | |
479 | { | |
480 | struct hostent *hp; | |
481 | struct sockaddr_in sa, lsa; | |
482 | int sock; | |
483 | ||
484 | memset(&lsa, 0, sizeof(lsa)); | |
485 | if (lhost) | |
486 | { | |
487 | if ((hp = gethostbyname(lhost)) != NULL) | |
488 | { | |
489 | memcpy((char *) &lsa.sin_addr, hp->h_addr, hp->h_length); | |
490 | lsa.sin_family = hp->h_addrtype; | |
491 | } | |
492 | else | |
493 | { | |
494 | lhost = NULL; | |
495 | } | |
496 | } | |
497 | if (lport) | |
498 | lsa.sin_port = htons((unsigned short) lport); | |
499 | ||
500 | memset(&sa, 0, sizeof(sa)); | |
501 | if (!(hp = gethostbyname(host))) | |
502 | return -1; | |
503 | memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length); | |
504 | sa.sin_family = hp->h_addrtype; | |
505 | sa.sin_port = htons((unsigned short) port); | |
506 | ||
507 | if ((sock = socket(sa.sin_family, SOCK_STREAM, 0)) < 0) | |
508 | return -1; | |
509 | ||
510 | if ((lhost || lport) | |
511 | && bind(sock, (struct sockaddr *) &lsa, sizeof(lsa)) < 0) | |
512 | { | |
513 | int errno_save = errno; | |
514 | close(sock); | |
515 | errno = errno_save; | |
516 | return -1; | |
517 | } | |
518 | ||
519 | if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) | |
520 | { | |
521 | int errno_save = errno; | |
522 | close(sock); | |
523 | errno = errno_save; | |
524 | return -1; | |
525 | } | |
526 | ||
527 | return sock; | |
528 | } | |
529 | ||
530 | /*************************************************************************/ | |
531 | ||
532 | void disconn(int s) | |
533 | { | |
534 | shutdown(s, 2); | |
535 | close(s); | |
536 | } | |
537 | ||
538 | /*************************************************************************/ |