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