NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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 "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;
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_socket_timeout = cs_subset_number(NeoMutt->sub, "socket_timeout");
86 if (c_socket_timeout > 0)
87 alarm(c_socket_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 (c_socket_timeout > 0)
100 {
101 const struct timeval tv = { c_socket_timeout, 0 };
102 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
103 {
104 mutt_debug(LL_DEBUG2, "Cannot set socket receive timeout. errno: %d\n", errno);
105 }
106 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
107 {
108 mutt_debug(LL_DEBUG2, "Cannot set socket send timeout. errno: %d\n", errno);
109 }
110 }
111
112 if (connect(fd, sa, sa_size) < 0)
113 {
114 save_errno = errno;
115 mutt_debug(LL_DEBUG2, "Connection failed. errno: %d\n", errno);
116 SigInt = false; /* reset in case we caught SIGINTR while in connect() */
117 }
118
119 if (c_socket_timeout > 0)
120 alarm(0);
122 sigprocmask(SIG_UNBLOCK, &set, NULL);
123
124 return save_errno;
125}
126
131{
132 int rc;
133
134 char *host_idna = NULL;
135
136#ifdef HAVE_GETADDRINFO
137 /* --- IPv4/6 --- */
138
139 /* "65536\0" */
140 char port[6] = { 0 };
141 struct addrinfo hints;
142 struct addrinfo *res = NULL;
143 struct addrinfo *cur = NULL;
144
145 /* we accept v4 or v6 STREAM sockets */
146 memset(&hints, 0, sizeof(hints));
147
148 const bool c_use_ipv6 = cs_subset_bool(NeoMutt->sub, "use_ipv6");
149 if (c_use_ipv6)
150 hints.ai_family = AF_UNSPEC;
151 else
152 hints.ai_family = AF_INET;
153
154 hints.ai_socktype = SOCK_STREAM;
155
156 snprintf(port, sizeof(port), "%d", conn->account.port);
157
158#ifdef HAVE_LIBIDN
159 if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
160 {
161 mutt_error(_("Bad IDN: '%s'"), conn->account.host);
162 return -1;
163 }
164#else
165 host_idna = conn->account.host;
166#endif
167
168 if (!OptNoCurses)
169 mutt_message(_("Looking up %s..."), conn->account.host);
170
171 rc = getaddrinfo(host_idna, port, &hints, &res);
172
173#ifdef HAVE_LIBIDN
174 FREE(&host_idna);
175#endif
176
177 if (rc)
178 {
179 mutt_error(_("Could not find the host \"%s\""), conn->account.host);
180 return -1;
181 }
182
183 if (!OptNoCurses)
184 mutt_message(_("Connecting to %s..."), conn->account.host);
185
186 rc = -1;
187 for (cur = res; cur; cur = cur->ai_next)
188 {
189 int fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
190 if (fd >= 0)
191 {
192 rc = socket_connect(fd, cur->ai_addr);
193 if (rc == 0)
194 {
195 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
196 conn->fd = fd;
197 break;
198 }
199 else
200 {
201 close(fd);
202 }
203 }
204 }
205
206 freeaddrinfo(res);
207#else
208 /* --- IPv4 only --- */
209
210 struct sockaddr_in sin;
211 struct hostent *he = NULL;
212
213 memset(&sin, 0, sizeof(sin));
214 sin.sin_port = htons(conn->account.port);
215 sin.sin_family = AF_INET;
216
217#ifdef HAVE_LIBIDN
218 if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
219 {
220 mutt_error(_("Bad IDN: '%s'"), conn->account.host);
221 return -1;
222 }
223#else
224 host_idna = conn->account.host;
225#endif
226
227 if (!OptNoCurses)
228 mutt_message(_("Looking up %s..."), conn->account.host);
229
230 he = gethostbyname(host_idna);
231
232#ifdef HAVE_LIBIDN
233 FREE(&host_idna);
234#endif
235
236 if (!he)
237 {
238 mutt_error(_("Could not find the host \"%s\""), conn->account.host);
239
240 return -1;
241 }
242
243 if (!OptNoCurses)
244 mutt_message(_("Connecting to %s..."), conn->account.host);
245
246 rc = -1;
247 for (int i = 0; he->h_addr_list[i]; i++)
248 {
249 memcpy(&sin.sin_addr, he->h_addr_list[i], he->h_length);
250 int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
251
252 if (fd >= 0)
253 {
254 rc = socket_connect(fd, (struct sockaddr *) &sin);
255 if (rc == 0)
256 {
257 fcntl(fd, F_SETFD, FD_CLOEXEC);
258 conn->fd = fd;
259 break;
260 }
261 else
262 {
263 close(fd);
264 }
265 }
266 }
267#endif
268 if (rc)
269 {
270 mutt_error(_("Could not connect to %s (%s)"), conn->account.host,
271 (rc > 0) ? strerror(rc) : _("unknown error"));
272 return -1;
273 }
274
275 return 0;
276}
277
281int raw_socket_read(struct Connection *conn, char *buf, size_t count)
282{
283 int rc;
284
286 do
287 {
288 rc = read(conn->fd, buf, count);
289 } while (rc < 0 && (errno == EINTR));
290
291 if (rc < 0)
292 {
293 mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
294 SigInt = false;
295 }
297
298 if (SigInt)
299 {
300 mutt_error(_("Connection to %s has been aborted"), conn->account.host);
301 SigInt = false;
302 rc = -1;
303 }
304
305 return rc;
306}
307
311int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
312{
313 int rc;
314 size_t sent = 0;
315
317 do
318 {
319 do
320 {
321 rc = write(conn->fd, buf + sent, count - sent);
322 } while (rc < 0 && (errno == EINTR));
323
324 if (rc < 0)
325 {
326 mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
328 return -1;
329 }
330
331 sent += rc;
332 } while ((sent < count) && !SigInt);
333
335 return sent;
336}
337
341int raw_socket_poll(struct Connection *conn, time_t wait_secs)
342{
343 if (conn->fd < 0)
344 return -1;
345
346 fd_set rfds;
347 struct timeval tv;
348
349 uint64_t wait_millis = wait_secs * 1000UL;
350
351 while (true)
352 {
353 tv.tv_sec = wait_millis / 1000;
354 tv.tv_usec = (wait_millis % 1000) * 1000;
355
356 FD_ZERO(&rfds);
357 FD_SET(conn->fd, &rfds);
358
359 uint64_t pre_t = mutt_date_now_ms();
360 const int rc = select(conn->fd + 1, &rfds, NULL, NULL, &tv);
361 uint64_t post_t = mutt_date_now_ms();
362
363 if ((rc > 0) || ((rc < 0) && (errno != EINTR)))
364 return rc;
365
366 if (SigInt)
368
369 wait_millis += pre_t;
370 if (wait_millis <= post_t)
371 return 0;
372 wait_millis -= post_t;
373 }
374}
375
380{
381 return close(conn->fd);
382}
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:140
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:79
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:59
int raw_socket_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
Definition: raw.c:379
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: raw.c:130
int raw_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll() -.
Definition: raw.c:341
int raw_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from a socket - Implements Connection::read() -.
Definition: raw.c:281
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write() -.
Definition: raw.c:311
#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:89
@ 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:455
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
GUI display the mailboxes in a side panel.
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:251
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:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45