NeoMutt  2022-04-29-249-gaae397
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 "mutt_globals.h"
51#include "options.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;
72
73 if (sa->sa_family == AF_INET)
74 sa_size = sizeof(struct sockaddr_in);
75#ifdef HAVE_GETADDRINFO
76 else if (sa->sa_family == AF_INET6)
77 sa_size = sizeof(struct sockaddr_in6);
78#endif
79 else
80 {
81 mutt_debug(LL_DEBUG1, "Unknown address family!\n");
82 return -1;
83 }
84
85 const short c_connect_timeout = cs_subset_number(NeoMutt->sub, "connect_timeout");
86 if (c_connect_timeout > 0)
87 alarm(c_connect_timeout);
88
90
91 /* FreeBSD's connect() does not respect SA_RESTART, meaning
92 * a SIGWINCH will cause the connect to fail. */
93 sigemptyset(&set);
94 sigaddset(&set, SIGWINCH);
95 sigprocmask(SIG_BLOCK, &set, NULL);
96
97 save_errno = 0;
98
99 if (connect(fd, sa, sa_size) < 0)
100 {
101 save_errno = errno;
102 mutt_debug(LL_DEBUG2, "Connection failed. errno: %d\n", errno);
103 SigInt = false; /* reset in case we caught SIGINTR while in connect() */
104 }
105
106 if (c_connect_timeout > 0)
107 alarm(0);
109 sigprocmask(SIG_UNBLOCK, &set, NULL);
110
111 return save_errno;
112}
113
118{
119 int rc;
120
121 char *host_idna = NULL;
122
123#ifdef HAVE_GETADDRINFO
124 /* --- IPv4/6 --- */
125
126 /* "65536\0" */
127 char port[6] = { 0 };
128 struct addrinfo hints;
129 struct addrinfo *res = NULL;
130 struct addrinfo *cur = NULL;
131
132 /* we accept v4 or v6 STREAM sockets */
133 memset(&hints, 0, sizeof(hints));
134
135 const bool c_use_ipv6 = cs_subset_bool(NeoMutt->sub, "use_ipv6");
136 if (c_use_ipv6)
137 hints.ai_family = AF_UNSPEC;
138 else
139 hints.ai_family = AF_INET;
140
141 hints.ai_socktype = SOCK_STREAM;
142
143 snprintf(port, sizeof(port), "%d", conn->account.port);
144
145#ifdef HAVE_LIBIDN
146 if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
147 {
148 mutt_error(_("Bad IDN: '%s'"), conn->account.host);
149 return -1;
150 }
151#else
152 host_idna = conn->account.host;
153#endif
154
155 if (!OptNoCurses)
156 mutt_message(_("Looking up %s..."), conn->account.host);
157
158 rc = getaddrinfo(host_idna, port, &hints, &res);
159
160#ifdef HAVE_LIBIDN
161 FREE(&host_idna);
162#endif
163
164 if (rc)
165 {
166 mutt_error(_("Could not find the host \"%s\""), conn->account.host);
167 return -1;
168 }
169
170 if (!OptNoCurses)
171 mutt_message(_("Connecting to %s..."), conn->account.host);
172
173 rc = -1;
174 for (cur = res; cur; cur = cur->ai_next)
175 {
176 int fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
177 if (fd >= 0)
178 {
179 rc = socket_connect(fd, cur->ai_addr);
180 if (rc == 0)
181 {
182 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
183 conn->fd = fd;
184 break;
185 }
186 else
187 close(fd);
188 }
189 }
190
191 freeaddrinfo(res);
192#else
193 /* --- IPv4 only --- */
194
195 struct sockaddr_in sin;
196 struct hostent *he = NULL;
197
198 memset(&sin, 0, sizeof(sin));
199 sin.sin_port = htons(conn->account.port);
200 sin.sin_family = AF_INET;
201
202#ifdef HAVE_LIBIDN
203 if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
204 {
205 mutt_error(_("Bad IDN: '%s'"), conn->account.host);
206 return -1;
207 }
208#else
209 host_idna = conn->account.host;
210#endif
211
212 if (!OptNoCurses)
213 mutt_message(_("Looking up %s..."), conn->account.host);
214
215 he = gethostbyname(host_idna);
216
217#ifdef HAVE_LIBIDN
218 FREE(&host_idna);
219#endif
220
221 if (!he)
222 {
223 mutt_error(_("Could not find the host \"%s\""), conn->account.host);
224
225 return -1;
226 }
227
228 if (!OptNoCurses)
229 mutt_message(_("Connecting to %s..."), conn->account.host);
230
231 rc = -1;
232 for (int i = 0; he->h_addr_list[i]; i++)
233 {
234 memcpy(&sin.sin_addr, he->h_addr_list[i], he->h_length);
235 int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
236
237 if (fd >= 0)
238 {
239 rc = socket_connect(fd, (struct sockaddr *) &sin);
240 if (rc == 0)
241 {
242 fcntl(fd, F_SETFD, FD_CLOEXEC);
243 conn->fd = fd;
244 break;
245 }
246 else
247 close(fd);
248 }
249 }
250#endif
251 if (rc)
252 {
253 mutt_error(_("Could not connect to %s (%s)"), conn->account.host,
254 (rc > 0) ? strerror(rc) : _("unknown error"));
255 return -1;
256 }
257
258 return 0;
259}
260
264int raw_socket_read(struct Connection *conn, char *buf, size_t count)
265{
266 int rc;
267
269 do
270 {
271 rc = read(conn->fd, buf, count);
272 } while (rc < 0 && (errno == EINTR));
273
274 if (rc < 0)
275 {
276 mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
277 SigInt = false;
278 }
280
281 if (SigInt)
282 {
283 mutt_error(_("Connection to %s has been aborted"), conn->account.host);
284 SigInt = false;
285 rc = -1;
286 }
287
288 return rc;
289}
290
294int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
295{
296 int rc;
297 size_t sent = 0;
298
300 do
301 {
302 do
303 {
304 rc = write(conn->fd, buf + sent, count - sent);
305 } while (rc < 0 && (errno == EINTR));
306
307 if (rc < 0)
308 {
309 mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
311 return -1;
312 }
313
314 sent += rc;
315 } while ((sent < count) && !SigInt);
316
318 return sent;
319}
320
324int raw_socket_poll(struct Connection *conn, time_t wait_secs)
325{
326 if (conn->fd < 0)
327 return -1;
328
329 fd_set rfds;
330 struct timeval tv;
331
332 uint64_t wait_millis = wait_secs * 1000UL;
333
334 while (true)
335 {
336 tv.tv_sec = wait_millis / 1000;
337 tv.tv_usec = (wait_millis % 1000) * 1000;
338
339 FD_ZERO(&rfds);
340 FD_SET(conn->fd, &rfds);
341
342 uint64_t pre_t = mutt_date_epoch_ms();
343 const int rc = select(conn->fd + 1, &rfds, NULL, NULL, &tv);
344 uint64_t post_t = mutt_date_epoch_ms();
345
346 if ((rc > 0) || ((rc < 0) && (errno != EINTR)))
347 return rc;
348
349 if (SigInt)
351
352 wait_millis += pre_t;
353 if (wait_millis <= post_t)
354 return 0;
355 wait_millis -= post_t;
356 }
357}
358
363{
364 return close(conn->fd);
365}
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:335
uint64_t mutt_date_epoch_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition: date.c:437
int raw_socket_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
Definition: raw.c:362
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: raw.c:117
int raw_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll() -.
Definition: raw.c:324
int raw_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from a socket - Implements Connection::read() -.
Definition: raw.c:264
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write() -.
Definition: raw.c:294
#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)
@ 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
Hundreds of global variables to back the user variables.
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:69
Handling of global boolean variables.
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:53
static int socket_connect(int fd, struct sockaddr *sa)
Set up to connect to a socket fd.
Definition: raw.c:67
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