]> jfr.im git - irc/thales.git/blame - src/sockutil.c
readmes added, contribs removed
[irc/thales.git] / src / sockutil.c
CommitLineData
2ace9480 1/* Thales - IRC to Relational Database Gateway
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
22extern int debug;
23
24/*************************************************************************/
25/*************************************************************************/
26
27/* Read from a socket with buffering. */
28
29static char read_netbuf[NET_BUFSIZE];
30static char *read_curpos = read_netbuf; /* Next byte to return */
31static char *read_bufend = read_netbuf; /* Next position for data from socket */
32static char *const read_buftop = read_netbuf + NET_BUFSIZE;
33int total_read = 0;
34
35
36/* Return amount of data in read buffer. */
37
38int 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
49static 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)
82 log("debug: buffered_read wanted %d, got %d", maxread, nread);
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 {
116 log("debug: buffered_read(%d,%p,%d) returning %d",
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
126static 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)
158 log("debug: buffered_read_one wanted %d, got %d", maxread, nread);
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)
168 log("debug: buffered_read_one(%d) returning %d", fd, EOF);
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)
177 log("debug: buffered_read_one(%d) returning %d", fd, c);
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
186static char write_netbuf[NET_BUFSIZE];
187static char *write_curpos = write_netbuf; /* Next byte to write to socket */
188static char *write_bufend = write_netbuf; /* Next position for data to socket */
189static char *const write_buftop = write_netbuf + NET_BUFSIZE;
190static int write_fd = -1;
191int total_written;
192
193
194/* Return amount of data in write buffer. */
195
196int 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
208static 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)
230 log("debug: flush_write_buffer wanted %d, got %d", maxwrite,
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
248static 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 {
312 log("debug: buffered_write(%d,%p,%d) returning %d",
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
324static 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)
346 log("debug: buffered_write_one(%d) returning %d", fd, EOF);
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)
360 log("debug: buffered_write_one(%d) returning %d", fd, c);
361 return (int) c & 0xFF;
362}
363#endif /* 0 */
364
365/*************************************************************************/
366/*************************************************************************/
367
368static int lastchar = EOF;
369
370int 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
383int 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
394char *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
429char *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
448int sread(int s, char *buf, int len)
449{
450 return buffered_read(s, buf, len);
451}
452
453/*************************************************************************/
454
455int sputs(char *str, int s)
456{
457 return buffered_write(s, str, strlen(str));
458}
459
460/*************************************************************************/
461
462int 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
478int 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
532void disconn(int s)
533{
534 shutdown(s, 2);
535 close(s);
536}
537
538/*************************************************************************/