NeoMutt  2024-04-16-36-g75b6fb
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 <unistd.h>
43#include "private.h"
44#include "mutt/lib.h"
45#include "config/lib.h"
46#include "core/lib.h"
47#include "gui/lib.h"
48#include "connaccount.h"
49#include "connection.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 struct sigaction oldalrm = { 0 };
72 struct sigaction act = { 0 };
73
74 if (sa->sa_family == AF_INET)
75 sa_size = sizeof(struct sockaddr_in);
76#ifdef HAVE_GETADDRINFO
77 else if (sa->sa_family == AF_INET6)
78 sa_size = sizeof(struct sockaddr_in6);
79#endif
80 else
81 {
82 mutt_debug(LL_DEBUG1, "Unknown address family!\n");
83 return -1;
84 }
85
86 /* Batch mode does not call mutt_signal_init(), so ensure the alarm
87 * interrupts the connect call */
88 const short c_socket_timeout = cs_subset_number(NeoMutt->sub, "socket_timeout");
89 if (c_socket_timeout > 0)
90 {
91 sigemptyset(&act.sa_mask);
92 act.sa_handler = mutt_sig_empty_handler;
93#ifdef SA_INTERRUPT
94 act.sa_flags = SA_INTERRUPT;
95#else
96 act.sa_flags = 0;
97#endif
98 sigaction(SIGALRM, &act, &oldalrm);
99 alarm(c_socket_timeout);
100 }
101
103
104 /* FreeBSD's connect() does not respect SA_RESTART, meaning
105 * a SIGWINCH will cause the connect to fail. */
106 sigemptyset(&set);
107 sigaddset(&set, SIGWINCH);
108 sigprocmask(SIG_BLOCK, &set, NULL);
109
110 save_errno = 0;
111
112 if (c_socket_timeout > 0)
113 {
114 const struct timeval tv = { c_socket_timeout, 0 };
115 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
116 {
117 mutt_debug(LL_DEBUG2, "Cannot set socket receive timeout. errno: %d\n", errno);
118 }
119 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
120 {
121 mutt_debug(LL_DEBUG2, "Cannot set socket send timeout. errno: %d\n", errno);
122 }
123 }
124
125 if (connect(fd, sa, sa_size) < 0)
126 {
127 save_errno = errno;
128 mutt_debug(LL_DEBUG2, "Connection failed. errno: %d\n", errno);
129 SigInt = false; /* reset in case we caught SIGINTR while in connect() */
130 }
131
132 if (c_socket_timeout > 0)
133 {
134 alarm(0);
135 sigaction(SIGALRM, &oldalrm, NULL);
136 }
138 sigprocmask(SIG_UNBLOCK, &set, NULL);
139
140 return save_errno;
141}
142
147{
148 int rc;
149
150 char *host_idna = NULL;
151
152#ifdef HAVE_GETADDRINFO
153 /* --- IPv4/6 --- */
154
155 /* "65536\0" */
156 char port[6] = { 0 };
157 struct addrinfo hints = { 0 };
158 struct addrinfo *res = NULL;
159 struct addrinfo *cur = NULL;
160
161 /* we accept v4 or v6 STREAM sockets */
162 const bool c_use_ipv6 = cs_subset_bool(NeoMutt->sub, "use_ipv6");
163 if (c_use_ipv6)
164 hints.ai_family = AF_UNSPEC;
165 else
166 hints.ai_family = AF_INET;
167
168 hints.ai_socktype = SOCK_STREAM;
169
170 snprintf(port, sizeof(port), "%d", conn->account.port);
171
172#ifdef HAVE_LIBIDN
173 if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
174 {
175 mutt_error(_("Bad IDN: '%s'"), conn->account.host);
176 return -1;
177 }
178#else
179 host_idna = conn->account.host;
180#endif
181
182 if (!OptNoCurses)
183 mutt_message(_("Looking up %s..."), conn->account.host);
184
185 rc = getaddrinfo(host_idna, port, &hints, &res);
186
187#ifdef HAVE_LIBIDN
188 FREE(&host_idna);
189#endif
190
191 if (rc)
192 {
193 mutt_error(_("Could not find the host \"%s\""), conn->account.host);
194 return -1;
195 }
196
197 if (!OptNoCurses)
198 mutt_message(_("Connecting to %s..."), conn->account.host);
199
200 rc = -1;
201 for (cur = res; cur; cur = cur->ai_next)
202 {
203 int fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
204 if (fd >= 0)
205 {
206 rc = socket_connect(fd, cur->ai_addr);
207 if (rc == 0)
208 {
209 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
210 conn->fd = fd;
211 break;
212 }
213 else
214 {
215 close(fd);
216 }
217 }
218 }
219
220 freeaddrinfo(res);
221#else
222 /* --- IPv4 only --- */
223
224 struct hostent *he = NULL;
225 struct sockaddr_in sin = { 0 };
226 sin.sin_port = htons(conn->account.port);
227 sin.sin_family = AF_INET;
228
229#ifdef HAVE_LIBIDN
230 if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
231 {
232 mutt_error(_("Bad IDN: '%s'"), conn->account.host);
233 return -1;
234 }
235#else
236 host_idna = conn->account.host;
237#endif
238
239 if (!OptNoCurses)
240 mutt_message(_("Looking up %s..."), conn->account.host);
241
242 he = gethostbyname(host_idna);
243
244#ifdef HAVE_LIBIDN
245 FREE(&host_idna);
246#endif
247
248 if (!he)
249 {
250 mutt_error(_("Could not find the host \"%s\""), conn->account.host);
251
252 return -1;
253 }
254
255 if (!OptNoCurses)
256 mutt_message(_("Connecting to %s..."), conn->account.host);
257
258 rc = -1;
259 for (int i = 0; he->h_addr_list[i]; i++)
260 {
261 memcpy(&sin.sin_addr, he->h_addr_list[i], he->h_length);
262 int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
263
264 if (fd >= 0)
265 {
266 rc = socket_connect(fd, (struct sockaddr *) &sin);
267 if (rc == 0)
268 {
269 fcntl(fd, F_SETFD, FD_CLOEXEC);
270 conn->fd = fd;
271 break;
272 }
273 else
274 {
275 close(fd);
276 }
277 }
278 }
279#endif
280 if (rc)
281 {
282 mutt_error(_("Could not connect to %s (%s)"), conn->account.host,
283 (rc > 0) ? strerror(rc) : _("unknown error"));
284 return -1;
285 }
286
287 return 0;
288}
289
293int raw_socket_read(struct Connection *conn, char *buf, size_t count)
294{
295 int rc;
296
298 do
299 {
300 rc = read(conn->fd, buf, count);
301 } while (rc < 0 && (errno == EINTR));
302
303 if (rc < 0)
304 {
305 mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
306 SigInt = false;
307 }
309
310 if (SigInt)
311 {
312 mutt_error(_("Connection to %s has been aborted"), conn->account.host);
313 SigInt = false;
314 rc = -1;
315 }
316
317 return rc;
318}
319
323int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
324{
325 int rc;
326 size_t sent = 0;
327
329 do
330 {
331 do
332 {
333 rc = write(conn->fd, buf + sent, count - sent);
334 } while (rc < 0 && (errno == EINTR));
335
336 if (rc < 0)
337 {
338 mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
340 return -1;
341 }
342
343 sent += rc;
344 } while ((sent < count) && !SigInt);
345
347 return sent;
348}
349
353int raw_socket_poll(struct Connection *conn, time_t wait_secs)
354{
355 if (conn->fd < 0)
356 return -1;
357
358 fd_set rfds = { 0 };
359 struct timeval tv = { 0 };
360
361 uint64_t wait_millis = wait_secs * 1000UL;
362
363 while (true)
364 {
365 tv.tv_sec = wait_millis / 1000;
366 tv.tv_usec = (wait_millis % 1000) * 1000;
367
368 FD_ZERO(&rfds);
369 FD_SET(conn->fd, &rfds);
370
371 uint64_t pre_t = mutt_date_now_ms();
372 const int rc = select(conn->fd + 1, &rfds, NULL, NULL, &tv);
373 uint64_t post_t = mutt_date_now_ms();
374
375 if ((rc > 0) || ((rc < 0) && (errno != EINTR)))
376 return rc;
377
378 if (SigInt)
380
381 wait_millis += pre_t;
382 if (wait_millis <= post_t)
383 return 0;
384 wait_millis -= post_t;
385 }
386}
387
392{
393 return close(conn->fd);
394}
Email Address Handling.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
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:391
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: raw.c:146
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:353
int raw_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from a socket - Implements Connection::read() -.
Definition: raw.c:293
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write() -.
Definition: raw.c:323
#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:464
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_empty_handler(int sig)
Dummy signal handler.
Definition: signal.c:73
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition: signal.c:63
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:254
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:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45