NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
raw.c
Go to the documentation of this file.
1
32#include "config.h"
33#include <errno.h>
34#include <fcntl.h>
35#include <netdb.h>
36#include <netinet/in.h>
37#include <signal.h>
38#include <stdint.h>
39#include <stdio.h>
40#include <string.h>
41#include <sys/select.h>
42#include <sys/socket.h>
43#include <unistd.h>
44#include "private.h"
45#include "mutt/lib.h"
46#include "config/lib.h"
47#include "core/lib.h"
48#include "gui/lib.h"
49#include "lib.h"
50#include "globals.h"
51#ifdef HAVE_LIBIDN
52#include "address/lib.h"
53#endif
54#ifdef HAVE_GETADDRINFO
55#include <stdbool.h>
56#endif
57
66static int socket_connect(int fd, struct sockaddr *sa)
67{
68 int sa_size;
69 int save_errno;
70 sigset_t set;
71
72 if (sa->sa_family == AF_INET)
73 sa_size = sizeof(struct sockaddr_in);
74#ifdef HAVE_GETADDRINFO
75 else if (sa->sa_family == AF_INET6)
76 sa_size = sizeof(struct sockaddr_in6);
77#endif
78 else
79 {
80 mutt_debug(LL_DEBUG1, "Unknown address family!\n");
81 return -1;
82 }
83
84 const short c_socket_timeout = cs_subset_number(NeoMutt->sub, "socket_timeout");
85 if (c_socket_timeout > 0)
86 alarm(c_socket_timeout);
87
89
90 /* FreeBSD's connect() does not respect SA_RESTART, meaning
91 * a SIGWINCH will cause the connect to fail. */
92 sigemptyset(&set);
93 sigaddset(&set, SIGWINCH);
94 sigprocmask(SIG_BLOCK, &set, NULL);
95
96 save_errno = 0;
97
98 if (c_socket_timeout > 0)
99 {
100 const struct timeval tv = { c_socket_timeout, 0 };
101 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
102 {
103 mutt_debug(LL_DEBUG2, "Cannot set socket receive timeout. errno: %d\n", errno);
104 }
105 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
106 {
107 mutt_debug(LL_DEBUG2, "Cannot set socket send timeout. errno: %d\n", errno);
108 }
109 }
110
111 if (connect(fd, sa, sa_size) < 0)
112 {
113 save_errno = errno;
114 mutt_debug(LL_DEBUG2, "Connection failed. errno: %d\n", errno);
115 SigInt = false; /* reset in case we caught SIGINTR while in connect() */
116 }
117
118 if (c_socket_timeout > 0)
119 alarm(0);
121 sigprocmask(SIG_UNBLOCK, &set, NULL);
122
123 return save_errno;
124}
125
130{
131 int rc;
132
133 char *host_idna = NULL;
134
135#ifdef HAVE_GETADDRINFO
136 /* --- IPv4/6 --- */
137
138 /* "65536\0" */
139 char port[6] = { 0 };
140 struct addrinfo hints;
141 struct addrinfo *res = NULL;
142 struct addrinfo *cur = NULL;
143
144 /* we accept v4 or v6 STREAM sockets */
145 memset(&hints, 0, sizeof(hints));
146
147 const bool c_use_ipv6 = cs_subset_bool(NeoMutt->sub, "use_ipv6");
148 if (c_use_ipv6)
149 hints.ai_family = AF_UNSPEC;
150 else
151 hints.ai_family = AF_INET;
152
153 hints.ai_socktype = SOCK_STREAM;
154
155 snprintf(port, sizeof(port), "%d", conn->account.port);
156
157#ifdef HAVE_LIBIDN
158 if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
159 {
160 mutt_error(_("Bad IDN: '%s'"), conn->account.host);
161 return -1;
162 }
163#else
164 host_idna = conn->account.host;
165#endif
166
167 if (!OptNoCurses)
168 mutt_message(_("Looking up %s..."), conn->account.host);
169
170 rc = getaddrinfo(host_idna, port, &hints, &res);
171
172#ifdef HAVE_LIBIDN
173 FREE(&host_idna);
174#endif
175
176 if (rc)
177 {
178 mutt_error(_("Could not find the host \"%s\""), conn->account.host);
179 return -1;
180 }
181
182 if (!OptNoCurses)
183 mutt_message(_("Connecting to %s..."), conn->account.host);
184
185 rc = -1;
186 for (cur = res; cur; cur = cur->ai_next)
187 {
188 int fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
189 if (fd >= 0)
190 {
191 rc = socket_connect(fd, cur->ai_addr);
192 if (rc == 0)
193 {
194 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
195 conn->fd = fd;
196 break;
197 }
198 else
199 close(fd);
200 }
201 }
202
203 freeaddrinfo(res);
204#else
205 /* --- IPv4 only --- */
206
207 struct sockaddr_in sin;
208 struct hostent *he = NULL;
209
210 memset(&sin, 0, sizeof(sin));
211 sin.sin_port = htons(conn->account.port);
212 sin.sin_family = AF_INET;
213
214#ifdef HAVE_LIBIDN
215 if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
216 {
217 mutt_error(_("Bad IDN: '%s'"), conn->account.host);
218 return -1;
219 }
220#else
221 host_idna = conn->account.host;
222#endif
223
224 if (!OptNoCurses)
225 mutt_message(_("Looking up %s..."), conn->account.host);
226
227 he = gethostbyname(host_idna);
228
229#ifdef HAVE_LIBIDN
230 FREE(&host_idna);
231#endif
232
233 if (!he)
234 {
235 mutt_error(_("Could not find the host \"%s\""), conn->account.host);
236
237 return -1;
238 }
239
240 if (!OptNoCurses)
241 mutt_message(_("Connecting to %s..."), conn->account.host);
242
243 rc = -1;
244 for (int i = 0; he->h_addr_list[i]; i++)
245 {
246 memcpy(&sin.sin_addr, he->h_addr_list[i], he->h_length);
247 int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
248
249 if (fd >= 0)
250 {
251 rc = socket_connect(fd, (struct sockaddr *) &sin);
252 if (rc == 0)
253 {
254 fcntl(fd, F_SETFD, FD_CLOEXEC);
255 conn->fd = fd;
256 break;
257 }
258 else
259 close(fd);
260 }
261 }
262#endif
263 if (rc)
264 {
265 mutt_error(_("Could not connect to %s (%s)"), conn->account.host,
266 (rc > 0) ? strerror(rc) : _("unknown error"));
267 return -1;
268 }
269
270 return 0;
271}
272
276int raw_socket_read(struct Connection *conn, char *buf, size_t count)
277{
278 int rc;
279
281 do
282 {
283 rc = read(conn->fd, buf, count);
284 } while (rc < 0 && (errno == EINTR));
285
286 if (rc < 0)
287 {
288 mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
289 SigInt = false;
290 }
292
293 if (SigInt)
294 {
295 mutt_error(_("Connection to %s has been aborted"), conn->account.host);
296 SigInt = false;
297 rc = -1;
298 }
299
300 return rc;
301}
302
306int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
307{
308 int rc;
309 size_t sent = 0;
310
312 do
313 {
314 do
315 {
316 rc = write(conn->fd, buf + sent, count - sent);
317 } while (rc < 0 && (errno == EINTR));
318
319 if (rc < 0)
320 {
321 mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
323 return -1;
324 }
325
326 sent += rc;
327 } while ((sent < count) && !SigInt);
328
330 return sent;
331}
332
336int raw_socket_poll(struct Connection *conn, time_t wait_secs)
337{
338 if (conn->fd < 0)
339 return -1;
340
341 fd_set rfds;
342 struct timeval tv;
343
344 uint64_t wait_millis = wait_secs * 1000UL;
345
346 while (true)
347 {
348 tv.tv_sec = wait_millis / 1000;
349 tv.tv_usec = (wait_millis % 1000) * 1000;
350
351 FD_ZERO(&rfds);
352 FD_SET(conn->fd, &rfds);
353
354 uint64_t pre_t = mutt_date_now_ms();
355 const int rc = select(conn->fd + 1, &rfds, NULL, NULL, &tv);
356 uint64_t post_t = mutt_date_now_ms();
357
358 if ((rc > 0) || ((rc < 0) && (errno != EINTR)))
359 return rc;
360
361 if (SigInt)
363
364 wait_millis += pre_t;
365 if (wait_millis <= post_t)
366 return 0;
367 wait_millis -= post_t;
368 }
369}
370
375{
376 return close(conn->fd);
377}
Email Address Handling.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
void mutt_query_exit(void)
Ask the user if they want to leave NeoMutt.
Definition: curs_lib.c:334
uint64_t mutt_date_now_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition: date.c:437
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:81
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:58
int raw_socket_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
Definition: raw.c:374
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: raw.c:129
int raw_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll() -.
Definition: raw.c:336
int raw_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from a socket - Implements Connection::read() -.
Definition: raw.c:276
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write() -.
Definition: raw.c:306
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
Convenience wrapper for the gui headers.
int mutt_idna_to_ascii_lz(const char *input, char **output, uint8_t flags)
Convert a domain to Punycode.
Definition: idna.c:113
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
static int socket_connect(int fd, struct sockaddr *sa)
Set up to connect to a socket fd.
Definition: raw.c:66
GUI display the mailboxes in a side panel.
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
Key value store.
char host[128]
Server to login to.
Definition: connaccount.h:54
unsigned short port
Port to connect to.
Definition: connaccount.h:58
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
int fd
Socket file descriptor.
Definition: connection.h:54
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39