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