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