1 /**
2  * @file
3  * Low-level socket handling
4  *
5  * @authors
6  * Copyright (C) 1998,2000 Michael R. Elkins <me@mutt.org>
7  * Copyright (C) 1999-2006,2008 Brendan Cully <brendan@kublai.com>
8  * Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
9  *
10  * @copyright
11  * This program is free software: you can redistribute it and/or modify it under
12  * the terms of the GNU General Public License as published by the Free Software
13  * Foundation, either version 2 of the License, or (at your option) any later
14  * version.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License along with
22  * this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 /**
26  * @page conn_socket Low-level socket handling
27  *
28  * Low-level socket handling
29  */
30 
31 #include "config.h"
32 #include <errno.h>
33 #include <string.h>
34 #include <time.h>
35 #include "private.h"
36 #include "mutt/lib.h"
37 #include "config/lib.h"
38 #include "core/lib.h"
39 #include "socket.h"
40 #include "connaccount.h"
41 #include "connection.h"
42 #include "protos.h"
43 #include "ssl.h"
44 
45 /**
46  * socket_preconnect - Execute a command before opening a socket
47  * @retval 0  Success
48  * @retval >0 An errno, e.g. EPERM
49  */
socket_preconnect(void)50 static int socket_preconnect(void)
51 {
52   const char *const c_preconnect = cs_subset_string(NeoMutt->sub, "preconnect");
53   if (!c_preconnect)
54     return 0;
55 
56   mutt_debug(LL_DEBUG2, "Executing preconnect: %s\n", c_preconnect);
57   const int rc = mutt_system(c_preconnect);
58   mutt_debug(LL_DEBUG2, "Preconnect result: %d\n", rc);
59   if (rc != 0)
60   {
61     const int save_errno = errno;
62     mutt_perror(_("Preconnect command failed"));
63 
64     return save_errno;
65   }
66 
67   return 0;
68 }
69 
70 /**
71  * mutt_socket_open - Simple wrapper
72  * @param conn Connection to a server
73  * @retval  0 Success
74  * @retval -1 Error
75  */
mutt_socket_open(struct Connection * conn)76 int mutt_socket_open(struct Connection *conn)
77 {
78   int rc;
79 
80   if (socket_preconnect())
81     return -1;
82 
83   rc = conn->open(conn);
84 
85   mutt_debug(LL_DEBUG2, "Connected to %s:%d on fd=%d\n", conn->account.host,
86              conn->account.port, conn->fd);
87 
88   return rc;
89 }
90 
91 /**
92  * mutt_socket_close - Close a socket
93  * @param conn Connection to a server
94  * @retval  0 Success
95  * @retval -1 Error
96  */
mutt_socket_close(struct Connection * conn)97 int mutt_socket_close(struct Connection *conn)
98 {
99   if (!conn)
100     return 0;
101 
102   int rc = -1;
103 
104   if (conn->fd < 0)
105     mutt_debug(LL_DEBUG1, "Attempt to close closed connection\n");
106   else
107     rc = conn->close(conn);
108 
109   conn->fd = -1;
110   conn->ssf = 0;
111   conn->bufpos = 0;
112   conn->available = 0;
113 
114   return rc;
115 }
116 
117 /**
118  * mutt_socket_read - Read from a Connection
119  * @param conn Connection a server
120  * @param buf Buffer to store read data
121  * @param len length of the buffer
122  * @retval >0 Success, number of bytes read
123  * @retval -1 Error, see errno
124  */
mutt_socket_read(struct Connection * conn,char * buf,size_t len)125 int mutt_socket_read(struct Connection *conn, char *buf, size_t len)
126 {
127   return conn->read(conn, buf, len);
128 }
129 
130 /**
131  * mutt_socket_write - Write to a Connection
132  * @param conn Connection to a server
133  * @param buf Buffer with data to write
134  * @param len Length of data to write
135  * @retval >0 Number of bytes written
136  * @retval -1 Error
137  */
mutt_socket_write(struct Connection * conn,const char * buf,size_t len)138 int mutt_socket_write(struct Connection *conn, const char *buf, size_t len)
139 {
140   return conn->write(conn, buf, len);
141 }
142 
143 /**
144  * mutt_socket_write_d - Write data to a socket
145  * @param conn Connection to a server
146  * @param buf Buffer with data to write
147  * @param len Length of data to write
148  * @param dbg Debug level for logging
149  * @retval >0 Number of bytes written
150  * @retval -1 Error
151  */
mutt_socket_write_d(struct Connection * conn,const char * buf,int len,int dbg)152 int mutt_socket_write_d(struct Connection *conn, const char *buf, int len, int dbg)
153 {
154   int sent = 0;
155 
156   mutt_debug(dbg, "%d> %s", conn->fd, buf);
157 
158   if (conn->fd < 0)
159   {
160     mutt_debug(LL_DEBUG1, "attempt to write to closed connection\n");
161     return -1;
162   }
163 
164   while (sent < len)
165   {
166     const int rc = conn->write(conn, buf + sent, len - sent);
167     if (rc < 0)
168     {
169       mutt_debug(LL_DEBUG1, "error writing (%s), closing socket\n", strerror(errno));
170       mutt_socket_close(conn);
171 
172       return -1;
173     }
174 
175     if (rc < len - sent)
176       mutt_debug(LL_DEBUG3, "short write (%d of %d bytes)\n", rc, len - sent);
177 
178     sent += rc;
179   }
180 
181   return sent;
182 }
183 
184 /**
185  * mutt_socket_poll - Checks whether reads would block
186  * @param conn Connection to a server
187  * @param wait_secs How long to wait for a response
188  * @retval >0 There is data to read
189  * @retval  0 Read would block
190  * @retval -1 Connection doesn't support polling
191  */
mutt_socket_poll(struct Connection * conn,time_t wait_secs)192 int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
193 {
194   if (conn->bufpos < conn->available)
195     return conn->available - conn->bufpos;
196 
197   if (conn->poll)
198     return conn->poll(conn, wait_secs);
199 
200   return -1;
201 }
202 
203 /**
204  * mutt_socket_readchar - Simple read buffering to speed things up
205  * @param[in]  conn Connection to a server
206  * @param[out] c    Character that was read
207  * @retval  1 Success
208  * @retval -1 Error
209  */
mutt_socket_readchar(struct Connection * conn,char * c)210 int mutt_socket_readchar(struct Connection *conn, char *c)
211 {
212   if (conn->bufpos >= conn->available)
213   {
214     if (conn->fd >= 0)
215       conn->available = conn->read(conn, conn->inbuf, sizeof(conn->inbuf));
216     else
217     {
218       mutt_debug(LL_DEBUG1, "attempt to read from closed connection\n");
219       return -1;
220     }
221     conn->bufpos = 0;
222     if (conn->available == 0)
223     {
224       mutt_error(_("Connection to %s closed"), conn->account.host);
225     }
226     if (conn->available <= 0)
227     {
228       mutt_socket_close(conn);
229       return -1;
230     }
231   }
232   *c = conn->inbuf[conn->bufpos];
233   conn->bufpos++;
234   return 1;
235 }
236 
237 /**
238  * mutt_socket_readln_d - Read a line from a socket
239  * @param buf    Buffer to store the line
240  * @param buflen Length of data to write
241  * @param conn   Connection to a server
242  * @param dbg    Debug level for logging
243  * @retval >0 Success, number of bytes read
244  * @retval -1 Error
245  */
mutt_socket_readln_d(char * buf,size_t buflen,struct Connection * conn,int dbg)246 int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
247 {
248   char ch;
249   int i;
250 
251   for (i = 0; i < buflen - 1; i++)
252   {
253     if (mutt_socket_readchar(conn, &ch) != 1)
254     {
255       buf[i] = '\0';
256       return -1;
257     }
258 
259     if (ch == '\n')
260       break;
261     buf[i] = ch;
262   }
263 
264   /* strip \r from \r\n termination */
265   if (i && (buf[i - 1] == '\r'))
266     i--;
267   buf[i] = '\0';
268 
269   mutt_debug(dbg, "%d< %s\n", conn->fd, buf);
270 
271   /* number of bytes read, not strlen */
272   return i + 1;
273 }
274 
275 /**
276  * mutt_socket_new - Allocate and initialise a new connection
277  * @param type Type of the new Connection
278  * @retval ptr New Connection
279  */
mutt_socket_new(enum ConnectionType type)280 struct Connection *mutt_socket_new(enum ConnectionType type)
281 {
282   struct Connection *conn = mutt_mem_calloc(1, sizeof(struct Connection));
283   conn->fd = -1;
284 
285   if (type == MUTT_CONNECTION_TUNNEL)
286   {
287     mutt_tunnel_socket_setup(conn);
288   }
289   else if (type == MUTT_CONNECTION_SSL)
290   {
291     int rc = mutt_ssl_socket_setup(conn);
292     if (rc < 0)
293       FREE(&conn);
294   }
295   else
296   {
297     conn->read = raw_socket_read;
298     conn->write = raw_socket_write;
299     conn->open = raw_socket_open;
300     conn->close = raw_socket_close;
301     conn->poll = raw_socket_poll;
302   }
303 
304   return conn;
305 }
306 
307 /**
308  * mutt_socket_empty - Clear out any queued data
309  *
310  * The internal buffer is emptied and any data that has already arrived at this
311  * machine (in kernel buffers) is read and dropped.
312  */
mutt_socket_empty(struct Connection * conn)313 void mutt_socket_empty(struct Connection *conn)
314 {
315   if (!conn)
316     return;
317 
318   char buf[1024];
319   int bytes;
320 
321   while ((bytes = mutt_socket_poll(conn, 0)) > 0)
322   {
323     mutt_socket_read(conn, buf, MIN(bytes, sizeof(buf)));
324   }
325 }
326