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