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