1 /*
2 * Assorted helper functions for doing I/O.
3 *
4 * Copyright: This file may be distributed under version 2 of the GPL licence.
5 *
6 * $Id: util_io.c 2851 2010-03-06 18:16:56Z kuhlmann $
7 */
8
9 #include "climm.h"
10 #include <errno.h>
11 #if HAVE_SYS_TYPES_H
12 #include <sys/types.h>
13 #endif
14 #if HAVE_SYS_SOCKET_H
15 #include <sys/socket.h>
16 #endif
17 #if HAVE_SYS_STAT_H
18 #include <sys/stat.h>
19 #endif
20 #if HAVE_SYS_UN_H
21 #include <sys/un.h>
22 #endif
23 #include <fcntl.h>
24 #if HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 #include <assert.h>
28 #if HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #ifdef HAVE_SYS_SELECT_H
32 #include <sys/select.h>
33 #endif
34 #include <signal.h>
35 #include <stdarg.h>
36 #include "preferences.h"
37 #include "util_ui.h"
38 #include "connection.h"
39 #include "util_io.h"
40 #include "io/io_private.h"
41 #include "io/io_tcp.h"
42 #include "io/io_socks5.h"
43 #include "conv.h"
44 #include "util.h"
45 #include "contact.h"
46
UtilIOConnectTCP(Connection * conn)47 void UtilIOConnectTCP (Connection *conn)
48 {
49 if (ServerPrefVal (conn->serv, CO_S5USE))
50 return IOConnectSocks5 (conn);
51 return IOConnectTCP (conn);
52 }
53
UtilIOListenTCP(Connection * conn)54 void UtilIOListenTCP (Connection *conn)
55 {
56 if (ServerPrefVal (conn->serv, CO_S5USE))
57 return IOListenSocks5 (conn);
58 return IOListenTCP (conn);
59 }
60
io_util_accept(Connection * conn,Dispatcher * d,Connection * newc)61 io_err_t io_util_accept (Connection *conn, Dispatcher *d, Connection *newc)
62 {
63 if (!d)
64 return IO_NO_CONN;
65 if (d->flags != FLAG_OPEN)
66 return d->funcs->f_cting (conn, d);
67 if (d->funcs->f_accept)
68 return d->funcs->f_accept (conn, d, newc);
69 return io_util_accept (conn, d->next, newc);
70 }
71
UtilIOAccept(Connection * conn,Connection * newc)72 int UtilIOAccept (Connection *conn, Connection *newc)
73 {
74 return io_util_accept (conn, conn->dispatcher, newc);
75 }
76
io_util_read(Connection * conn,Dispatcher * d,char * buf,size_t count)77 io_err_t io_util_read (Connection *conn, Dispatcher *d, char *buf, size_t count)
78 {
79 if (!d)
80 return IO_NO_CONN;
81 if (d->flags != FLAG_OPEN)
82 return d->funcs->f_cting (conn, d);
83 if (d->funcs->f_read)
84 return d->funcs->f_read (conn, d, buf, count);
85 return io_util_read (conn, d->next, buf, count);
86 }
87
UtilIORead(Connection * conn,char * buf,size_t count)88 int UtilIORead (Connection *conn, char *buf, size_t count)
89 {
90 return io_util_read (conn, conn->dispatcher, buf, count);
91 }
92
io_util_write(Connection * conn,Dispatcher * d,const char * buf,size_t count)93 io_err_t io_util_write (Connection *conn, Dispatcher *d, const char *buf, size_t count)
94 {
95 if (!d)
96 return IO_NO_CONN;
97 if (d->flags != FLAG_OPEN)
98 return io_any_appendbuf (conn, d, buf, count);
99 if (d->funcs->f_write)
100 return d->funcs->f_write (conn, d, buf, count);
101 return io_util_write (conn, d->next, buf, count);
102 }
103
UtilIOWrite(Connection * conn,const char * buf,size_t count)104 io_err_t UtilIOWrite (Connection *conn, const char *buf, size_t count)
105 {
106 return io_util_write (conn, conn->dispatcher, buf, count);
107 }
108
io_any_appendbuf(Connection * conn,Dispatcher * d,const char * buf,size_t count)109 io_err_t io_any_appendbuf (Connection *conn, Dispatcher *d, const char *buf, size_t count)
110 {
111 char *newbuf;
112 conn->connect |= CONNECT_SELECT_W;
113 DebugH (DEB_TCP, "conn %p append %ld to %ld", conn, (long int)count, (long int)d->outlen);
114 if (!count)
115 return IO_OK;
116 if (d->outlen)
117 newbuf = realloc (d->outbuf, d->outlen + count);
118 else
119 newbuf = malloc (count);
120 if (!newbuf)
121 {
122 s_repl (&d->lasterr, "");
123 return IO_NO_MEM;
124 }
125 memcpy (newbuf + d->outlen, buf, count);
126 d->outlen += count;
127 d->outbuf = newbuf;
128 return IO_OK;
129 }
130
131
UtilIOClose(Connection * conn)132 void UtilIOClose (Connection *conn)
133 {
134 if (conn && conn->dispatcher && conn->dispatcher->funcs && conn->dispatcher->funcs->f_close)
135 conn->dispatcher->funcs->f_close (conn, conn->dispatcher);
136 conn->connect &= ~CONNECT_SELECT_A;
137 assert (!conn || conn->sok < 0);
138 assert (!conn || !conn->dispatcher);
139 }
140
UtilIOErr(Connection * conn)141 const char *UtilIOErr (Connection *conn)
142 {
143 return conn->dispatcher->funcs->f_err (conn, conn->dispatcher);
144 }
145
UtilIOSSLSupported(void)146 io_ssl_err_t UtilIOSSLSupported (void)
147 {
148 io_ssl_err_t rcgnutls = IOGnuTLSSupported ();
149 io_ssl_err_t rcopenssl = rcgnutls == IO_SSL_OK ? IO_SSL_NOLIB : IOOpenSSLSupported ();
150
151 if (rcgnutls == IO_SSL_OK || rcopenssl == IO_SSL_OK)
152 return IO_SSL_OK;
153 if (rcgnutls != IO_SSL_NOLIB)
154 {
155 rl_printf (i18n (2374, "SSL error: %s [%d]\n"), IOGnuTLSInitError (), 0);
156 rl_printf (i18n (2371, "SSL init failed.\n"));
157 }
158 if (rcopenssl == IO_SSL_INIT)
159 {
160 rl_printf (i18n (2374, "SSL error: %s [%d]\n"), IOOpenSSLInitError (), 0);
161 rl_printf (i18n (2371, "SSL init failed.\n"));
162 }
163 else
164 rl_printf (i18n (2581, "Install the GnuTLS library and enjoy encrypted connections to peers!\n"));
165 return IO_SSL_NOLIB;
166 }
167
UtilIOSSLOpen(Connection * conn,char is_client)168 io_ssl_err_t UtilIOSSLOpen (Connection *conn, char is_client)
169 {
170 if (IOGnuTLSSupported () == IO_SSL_OK)
171 return IOGnuTLSOpen (conn, is_client);
172 else if (IOOpenSSLSupported () == IO_SSL_OK)
173 return IOOpenSSLOpen (conn, is_client);
174 else
175 return IO_SSL_NOLIB;
176 }
177
UtilIOShowError(Connection * conn,io_err_t rc)178 io_err_t UtilIOShowError (Connection *conn, io_err_t rc)
179 {
180 int e = errno;
181 const char *t = NULL;
182
183 switch (rc) {
184
185 case IO_CONNECTED:
186 rl_print ("");
187 if (prG->verbose || (conn->serv && conn == conn->serv->conn))
188 if (rl_pos () > 0)
189 rl_print (i18n (1634, "ok.\n"));
190 return IO_CONNECTED;
191
192 case IO_OK:
193 return IO_OK;
194
195 case IO_NO_MEM:
196 case IO_NO_PARAM:
197 assert (0);
198
199 case IO_NO_SOCKET:
200 if (1) t = i18n (1638, "Couldn't create socket"); else
201 case IO_NO_NONBLOCK:
202 if (1) t = i18n (1950, "Couldn't set socket nonblocking"); else
203 case IO_NO_HOSTNAME:
204 if (1) t = i18n (2743, "Can't find hostname"); else
205 case IO_CONN_TO:
206 if (1) t = i18n (2744, "Connection timed out"); else
207 case IO_NO_CONN:
208 t = i18n (1952, "Couldn't open connection");
209 if (prG->verbose || (conn->serv && conn == conn->serv->conn))
210 {
211 Contact *cont = conn->cont;
212 char *semi = strchr (conn->server, ';');
213 if (semi)
214 *semi = 0;
215 rl_log_for (cont->nick, COLCONTACT);
216 rl_printf (i18n (2745, "Opening connection to %s:%s%ld%s "),
217 s_wordquote (conn->server), COLQUOTE, UD2UL (conn->port), COLNONE);
218 if (semi)
219 *semi = ';';
220 rl_print (i18n (1949, "failed:\n"));
221 rl_printf ("%s [%d]\n",
222 s_sprintf ("%s: %s (%d).", t, conn->dispatcher->funcs->f_err (conn, conn->dispatcher), e),
223 __LINE__);
224 }
225 UtilIOClose (conn);
226 return IO_RW;
227 case IO_CLOSED:
228 #ifdef ECONNRESET
229 if (!errno)
230 errno = ECONNRESET;
231 #endif
232 case IO_RW:
233 if (prG->verbose || (conn->serv && conn == conn->serv->conn))
234 {
235 Contact *cont;
236 if ((cont = conn->cont))
237 {
238 rl_log_for (cont->nick, COLCONTACT);
239 rl_printf (i18n (1878, "Error while reading from socket: %s (%d, %d)\n"), conn->dispatcher->funcs->f_err (conn, conn->dispatcher), rc, errno);
240 }
241 }
242 UtilIOClose (conn);
243 return IO_RW;
244 default:
245 assert (0);
246 }
247 }
248
249 /*
250 * Read a complete line from a fd.
251 *
252 * Returned string may not be free()d.
253 */
UtilIOReadline(FILE * fd)254 strc_t UtilIOReadline (FILE *fd)
255 {
256 static str_s str;
257 char *p;
258
259 s_init (&str, "", 256);
260 while (1)
261 {
262 str.txt[str.max - 2] = 0;
263 if (!fgets (str.txt + str.len, str.max - str.len, fd))
264 {
265 str.txt[str.len] = '\0';
266 if (!str.len)
267 return NULL;
268 break;
269 }
270 str.txt[str.max - 1] = '\0';
271 str.len = strlen (str.txt);
272 if (!str.txt[str.max - 2])
273 break;
274 s_blow (&str, 128);
275 }
276 if ((p = strpbrk (str.txt, "\r\n")))
277 {
278 *p = 0;
279 str.len = strlen (str.txt);
280 }
281 return &str;
282 }
283
284 static struct timeval tv;
285 static fd_set fds[3];
286 static int maxfd;
287
UtilIOSelectInit(int sec,int usec)288 void UtilIOSelectInit (int sec, int usec)
289 {
290 FD_ZERO (&fds[0]);
291 FD_ZERO (&fds[1]);
292 FD_ZERO (&fds[2]);
293 tv.tv_sec = sec;
294 tv.tv_usec = usec;
295 maxfd = 0;
296 }
297
UtilIOSelectAdd(FD_T sok,int nr)298 void UtilIOSelectAdd (FD_T sok, int nr)
299 {
300 FD_SET (sok, &fds[nr & 3]);
301 if (sok > maxfd)
302 maxfd = sok;
303 }
304
UtilIOSelectIs(FD_T sok,int nr)305 BOOL UtilIOSelectIs (FD_T sok, int nr)
306 {
307 return ((nr & READFDS) && FD_ISSET (sok, &fds[READFDS & 3]))
308 || ((nr & WRITEFDS) && FD_ISSET (sok, &fds[WRITEFDS & 3]))
309 || ((nr & EXCEPTFDS) && FD_ISSET (sok, &fds[EXCEPTFDS & 3]));
310 }
311
UtilIOSelect(void)312 void UtilIOSelect (void)
313 {
314 int res, rc;
315
316 errno = 0;
317 res = select (maxfd + 1, &fds[READFDS & 3], &fds[WRITEFDS & 3], &fds[EXCEPTFDS & 3], &tv);
318 rc = errno;
319 if (res == -1)
320 {
321 FD_ZERO (&fds[READFDS & 3]);
322 FD_ZERO (&fds[WRITEFDS & 3]);
323 FD_ZERO (&fds[EXCEPTFDS & 3]);
324 if (rc != EINTR && rc != EAGAIN)
325 {
326 printf (i18n (1849, "Error on select: %s (%d)\n"), strerror (rc), rc);
327 assert (0);
328 }
329 }
330 }
331