NeoMutt  2021-02-05-666-ge300cd
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 <sys/time.h>
44 #include <time.h>
45 #include <unistd.h>
46 #include "private.h"
47 #include "mutt/lib.h"
48 #include "config/lib.h"
49 #include "core/lib.h"
50 #include "gui/lib.h"
51 #include "lib.h"
52 #include "mutt_globals.h"
53 #include "options.h"
54 #ifdef HAVE_LIBIDN
55 #include "address/lib.h"
56 #endif
57 #ifdef HAVE_GETADDRINFO
58 #include <stdbool.h>
59 #endif
60 
69 static int socket_connect(int fd, struct sockaddr *sa)
70 {
71  int sa_size;
72  int save_errno;
73  sigset_t set;
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  const short c_connect_timeout =
88  cs_subset_number(NeoMutt->sub, "connect_timeout");
89  if (c_connect_timeout > 0)
90  alarm(c_connect_timeout);
91 
93 
94  /* FreeBSD's connect() does not respect SA_RESTART, meaning
95  * a SIGWINCH will cause the connect to fail. */
96  sigemptyset(&set);
97  sigaddset(&set, SIGWINCH);
98  sigprocmask(SIG_BLOCK, &set, NULL);
99 
100  save_errno = 0;
101 
102  if (connect(fd, sa, sa_size) < 0)
103  {
104  save_errno = errno;
105  mutt_debug(LL_DEBUG2, "Connection failed. errno: %d\n", errno);
106  SigInt = false; /* reset in case we caught SIGINTR while in connect() */
107  }
108 
109  if (c_connect_timeout > 0)
110  alarm(0);
112  sigprocmask(SIG_UNBLOCK, &set, NULL);
113 
114  return save_errno;
115 }
116 
120 int raw_socket_open(struct Connection *conn)
121 {
122  int rc;
123 
124  char *host_idna = NULL;
125 
126 #ifdef HAVE_GETADDRINFO
127  /* --- IPv4/6 --- */
128 
129  /* "65536\0" */
130  char port[6];
131  struct addrinfo hints;
132  struct addrinfo *res = NULL;
133  struct addrinfo *cur = NULL;
134 
135  /* we accept v4 or v6 STREAM sockets */
136  memset(&hints, 0, sizeof(hints));
137 
138  const bool c_use_ipv6 = cs_subset_bool(NeoMutt->sub, "use_ipv6");
139  if (c_use_ipv6)
140  hints.ai_family = AF_UNSPEC;
141  else
142  hints.ai_family = AF_INET;
143 
144  hints.ai_socktype = SOCK_STREAM;
145 
146  snprintf(port, sizeof(port), "%d", conn->account.port);
147 
148 #ifdef HAVE_LIBIDN
149  if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
150  {
151  mutt_error(_("Bad IDN: '%s'"), conn->account.host);
152  return -1;
153  }
154 #else
155  host_idna = conn->account.host;
156 #endif
157 
158  if (!OptNoCurses)
159  mutt_message(_("Looking up %s..."), conn->account.host);
160 
161  rc = getaddrinfo(host_idna, port, &hints, &res);
162 
163 #ifdef HAVE_LIBIDN
164  FREE(&host_idna);
165 #endif
166 
167  if (rc)
168  {
169  mutt_error(_("Could not find the host \"%s\""), conn->account.host);
170  return -1;
171  }
172 
173  if (!OptNoCurses)
174  mutt_message(_("Connecting to %s..."), conn->account.host);
175 
176  rc = -1;
177  for (cur = res; cur; cur = cur->ai_next)
178  {
179  int fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
180  if (fd >= 0)
181  {
182  rc = socket_connect(fd, cur->ai_addr);
183  if (rc == 0)
184  {
185  fcntl(fd, F_SETFD, FD_CLOEXEC);
186  conn->fd = fd;
187  break;
188  }
189  else
190  close(fd);
191  }
192  }
193 
194  freeaddrinfo(res);
195 #else
196  /* --- IPv4 only --- */
197 
198  struct sockaddr_in sin;
199  struct hostent *he = NULL;
200 
201  memset(&sin, 0, sizeof(sin));
202  sin.sin_port = htons(conn->account.port);
203  sin.sin_family = AF_INET;
204 
205 #ifdef HAVE_LIBIDN
206  if (mutt_idna_to_ascii_lz(conn->account.host, &host_idna, 1) != 0)
207  {
208  mutt_error(_("Bad IDN: '%s'"), conn->account.host);
209  return -1;
210  }
211 #else
212  host_idna = conn->account.host;
213 #endif
214 
215  if (!OptNoCurses)
216  mutt_message(_("Looking up %s..."), conn->account.host);
217 
218  he = gethostbyname(host_idna);
219 
220 #ifdef HAVE_LIBIDN
221  FREE(&host_idna);
222 #endif
223 
224  if (!he)
225  {
226  mutt_error(_("Could not find the host \"%s\""), conn->account.host);
227 
228  return -1;
229  }
230 
231  if (!OptNoCurses)
232  mutt_message(_("Connecting to %s..."), conn->account.host);
233 
234  rc = -1;
235  for (int i = 0; he->h_addr_list[i]; i++)
236  {
237  memcpy(&sin.sin_addr, he->h_addr_list[i], he->h_length);
238  int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
239 
240  if (fd >= 0)
241  {
242  rc = socket_connect(fd, (struct sockaddr *) &sin);
243  if (rc == 0)
244  {
245  fcntl(fd, F_SETFD, FD_CLOEXEC);
246  conn->fd = fd;
247  break;
248  }
249  else
250  close(fd);
251  }
252  }
253 #endif
254  if (rc)
255  {
256  mutt_error(_("Could not connect to %s (%s)"), conn->account.host,
257  (rc > 0) ? strerror(rc) : _("unknown error"));
258  return -1;
259  }
260 
261  return 0;
262 }
263 
267 int raw_socket_read(struct Connection *conn, char *buf, size_t count)
268 {
269  int rc;
270 
272  do
273  {
274  rc = read(conn->fd, buf, count);
275  } while (rc < 0 && (errno == EINTR));
276 
277  if (rc < 0)
278  {
279  mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
280  SigInt = false;
281  }
283 
284  if (SigInt)
285  {
286  mutt_error(_("Connection to %s has been aborted"), conn->account.host);
287  SigInt = false;
288  rc = -1;
289  }
290 
291  return rc;
292 }
293 
297 int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
298 {
299  int rc;
300  size_t sent = 0;
301 
303  do
304  {
305  do
306  {
307  rc = write(conn->fd, buf + sent, count - sent);
308  } while (rc < 0 && (errno == EINTR));
309 
310  if (rc < 0)
311  {
312  mutt_error(_("Error talking to %s (%s)"), conn->account.host, strerror(errno));
314  return -1;
315  }
316 
317  sent += rc;
318  } while ((sent < count) && !SigInt);
319 
321  return sent;
322 }
323 
327 int raw_socket_poll(struct Connection *conn, time_t wait_secs)
328 {
329  if (conn->fd < 0)
330  return -1;
331 
332  fd_set rfds;
333  struct timeval tv;
334 
335  uint64_t wait_millis = wait_secs * 1000UL;
336 
337  while (true)
338  {
339  tv.tv_sec = wait_millis / 1000;
340  tv.tv_usec = (wait_millis % 1000) * 1000;
341 
342  FD_ZERO(&rfds);
343  FD_SET(conn->fd, &rfds);
344 
345  uint64_t pre_t = mutt_date_epoch_ms();
346  const int rc = select(conn->fd + 1, &rfds, NULL, NULL, &tv);
347  uint64_t post_t = mutt_date_epoch_ms();
348 
349  if ((rc > 0) || ((rc < 0) && (errno != EINTR)))
350  return rc;
351 
352  if (SigInt)
353  mutt_query_exit();
354 
355  wait_millis += pre_t;
356  if (wait_millis <= post_t)
357  return 0;
358  wait_millis -= post_t;
359  }
360 }
361 
365 int raw_socket_close(struct Connection *conn)
366 {
367  return close(conn->fd);
368 }
Convenience wrapper for the gui headers.
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
uint64_t mutt_date_epoch_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition: date.c:436
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
#define mutt_error(...)
Definition: logging.h:88
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:67
Shared functions that are private to Connections.
#define _(a)
Definition: message.h:28
WHERE bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:47
void mutt_query_exit(void)
Ask the user if they want to leave NeoMutt.
Definition: curs_lib.c:403
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
Convenience wrapper for the config headers.
Email Address Handling.
char host[128]
Server to login to.
Definition: connaccount.h:53
int mutt_idna_to_ascii_lz(const char *input, char **output, uint8_t flags)
Log at debug level 2.
Definition: logging.h:41
static int socket_connect(int fd, struct sockaddr *sa)
set up to connect to a socket fd
Definition: raw.c:69
Convenience wrapper for the core headers.
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write() -.
Definition: raw.c:297
unsigned short port
Port to connect to.
Definition: connaccount.h:57
int fd
Socket file descriptor.
Definition: connection.h:44
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: raw.c:120
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
int raw_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll() -.
Definition: raw.c:327
int raw_socket_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
Definition: raw.c:365
int raw_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from a socket - Implements Connection::read() -.
Definition: raw.c:267
Log at debug level 1.
Definition: logging.h:40
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
Hundreds of global variables to back the user variables.
Handling of global boolean variables.
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Convenience wrapper for the library headers.