io.c 9.99 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
/* io.c
 *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Niels Mller
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Niels Möller's avatar
Niels Möller committed
24
25
 */

Niels Möller's avatar
Niels Möller committed
26
27
28
#include <assert.h>
#include <string.h>

Niels Möller's avatar
Niels Möller committed
29
30
31
32
33
34
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
Niels Möller's avatar
Niels Möller committed
35
#include <arpa/inet.h>
Niels Möller's avatar
Niels Möller committed
36

Niels Möller's avatar
Niels Möller committed
37
38
39
40
41
#include "io.h"
#include "werror.h"
#include "write_buffer.h"
#include "xalloc.h"

Niels Möller's avatar
Niels Möller committed
42
43
44
45
46
/* A little more than an hour */
#define MAX_TIMEOUT 4000

struct fd_read
{
47
  struct abstract_read super;
Niels Möller's avatar
Niels Möller committed
48
49
50
  int fd;
};

Niels Möller's avatar
Niels Möller committed
51
static int do_read(struct abstract_read **r, UINT32 length, UINT8 *buffer)
Niels Möller's avatar
Niels Möller committed
52
{
53
54
55
  struct fd_read *closure
    = (struct fd_read *) *r;

56
57
58
59
60
61
62
  while(1)
    {
      int res = read(closure->fd, buffer, length);
      if (!res)
	return A_EOF;
      if (res > 0)
	return res;
63

64
65
66
67
68
69
70
      switch(errno)
	{
	case EINTR:
	  continue;
	case EWOULDBLOCK:  /* aka EAGAIN */
	  return 0;
	default:
Niels Möller's avatar
Niels Möller committed
71
72
73
74
	  werror("io.c: do_read: read() failed (errno %d), %s\n",
		 errno, strerror(errno));
	  debug("  fd = %d, buffer = %p, length = %ud\n",
		closure->fd, buffer, length);
75
76
77
78
	  return A_FAIL;
	}
    }
}
Niels Möller's avatar
Niels Möller committed
79

80
81
82
#define FOR_FDS(type, fd, list, extra)				\
{								\
  type **(_fd);							\
Niels Möller's avatar
Niels Möller committed
83
  type *(fd);							\
Niels Möller's avatar
Niels Möller committed
84
  for(_fd = &(list); ((fd) = *_fd); (extra)) {
85
86


Niels Möller's avatar
Niels Möller committed
87
88
89
#define END_FOR_FDS _fd = &(*_fd)->next; } }

/* UNLINK_FD must be followed by a continue, to avoid updating _fd */
90
#define UNLINK_FD (*_fd = (*_fd)->next)
91

Niels Möller's avatar
Niels Möller committed
92
93
94
95
96
97
void io_run(struct io_backend *b)
{
  while(1)
    {
      struct pollfd *fds;
      int i;
98
      unsigned long nfds; /* FIXME: Should be nfds_t if that type is defined */
Niels Möller's avatar
Niels Möller committed
99
100
      int timeout;
      int res;
101

Niels Möller's avatar
Niels Möller committed
102
      nfds = b->nio + b->nlisten + b->nconnect;
Niels Möller's avatar
Niels Möller committed
103
104

      if (b->callouts)
105
106
107
108
109
110
111
112
113
114
115
116
	 {
	   time_t now = time(NULL);
	   if (now >= b->callouts->when)
	     timeout = 0;
	   else
	     {
	       if (b->callouts->when > now + MAX_TIMEOUT)
		 timeout = MAX_TIMEOUT * 1000;
	       else
		 timeout = (b->callouts->when - now) * 1000;
	     }
	 }
Niels Möller's avatar
Niels Möller committed
117
      else
118
119
120
121
122
123
124
	 {
	   if (!nfds)
	     /* All done */
	     break;
	   timeout = -1;
	 }

Niels Möller's avatar
Niels Möller committed
125
126
      fds = alloca(sizeof(struct pollfd) * nfds);

127
      /* Handle fds in order: write, read, accept, connect. */
Niels Möller's avatar
Niels Möller committed
128
      i = 0;
129

130
      FOR_FDS(struct io_fd, fd, b->io, i++)
131
132
133
134
135
136
137
138
139
140
	 {
	   fds[i].fd = fd->on_hold ? -1 : fd->fd;
	   fds[i].events = 0;
	   if (!fd->on_hold)
	     fds[i].events |= POLLIN;

	   /* pre_write returns 0 if the buffer is empty */
	   if (write_buffer_pre_write(fd->buffer))
	     fds[i].events |= POLLOUT;
	 }
141
142
      END_FOR_FDS;

Niels Möller's avatar
Niels Möller committed
143
      FOR_FDS(struct listen_fd, fd, b->listen, i++)
144
145
146
147
	 {
	   fds[i].fd = fd->fd;
	   fds[i].events = POLLIN;
	 }
148
149
150
      END_FOR_FDS;

      FOR_FDS(struct connect_fd, fd, b->connect, i++)
151
152
153
154
	 {
	   fds[i].fd = fd->fd;
	   fds[i].events = POLLOUT;
	 }
155
156
      END_FOR_FDS;

Niels Möller's avatar
Niels Möller committed
157
158
159
      res = poll(fds, nfds, timeout);

      if (!res)
160
161
162
163
164
165
166
	 {
	   /* Timeout. Run the callout */
	   struct callout *f = b->callouts;

	   if (!CALLBACK(f->callout))
	     fatal("What now?");
	   b->callouts = f->next;
Niels Möller's avatar
Niels Möller committed
167
	   lsh_free(f);
168
	 }
Niels Möller's avatar
Niels Möller committed
169
      if (res<0)
170
171
172
173
174
175
176
177
178
179
	 {
	   switch(errno)
	     {
	     case EAGAIN:
	     case EINTR:
	       continue;
	     default:
	       fatal("io_run:poll failed: %s", strerror(errno));
	     }
	 }
Niels Möller's avatar
Niels Möller committed
180
      else
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
	 { /* Process files */
	   i = 0;

	   /* Handle writing first */
	   FOR_FDS(struct io_fd, fd, b->io, i++)
	     {
	       if (fds[i].revents & POLLOUT)
		 {
		   UINT32 size = MIN(fd->buffer->end - fd->buffer->start,
				     fd->buffer->block_size);
		   int res = write(fd->fd,
				   fd->buffer->buffer + fd->buffer->start,
				   size);
		   if (!res)
		     fatal("Closed?");
		   if (res < 0)
		     switch(errno)
		       {
		       case EINTR:
		       case EAGAIN:
			 break;
		       default:
			 werror("io.c: write failed, %s\n", strerror(errno));
			 CALLBACK(fd->close_callback);

			 fd->please_close = 1;

			 break;
		       }
		   else if (!res)
		     fatal("What now?");
		   else
		     fd->buffer->start += res;
		 }
	     }
	   END_FOR_FDS;

	   /* Handle reading */
	   i = 0; /* Start over */
	   FOR_FDS(struct io_fd, fd, b->io, i++)
	     {
	       if (!fd->please_close
		   && (fds[i].revents & POLLIN))
		 {
		   struct fd_read r =
		   { { do_read }, fd->fd };

228
229
230
		   /* The handler function may install a new handler */
		   if (!READ_HANDLER(fd->handler,
				     &r.super))
231
		     {
232
233
234
235
236
237
		       /* FIXME: Perhaps we should not close yet, but
			* stop reading and close as soon as the write
			* buffer is flushed? But in that case, we
			* probably also want to set some flag on the
			* write buffer so that no more data can be
			* written into it. */
238
239
		       fd->please_close = 1;
		     }
240
		}
Niels Möller's avatar
Niels Möller committed
241
242
243
244
245
246
247
	      if (fd->please_close)
		{
		  /* FIXME: Cleanup properly...
		   *
		   * After a write error, read state must be freed,
		   * and vice versa. */
		  UNLINK_FD;
248
249
		  b->nio--;
		  if (fd->handler)
Niels Möller's avatar
Niels Möller committed
250
251
252
		    lsh_free(fd->handler);
		  lsh_free(fd->buffer);
		  lsh_free(fd);
Niels Möller's avatar
Niels Möller committed
253
		  continue;
Niels Möller's avatar
Niels Möller committed
254
		}
255
256
257
	    }
	  END_FOR_FDS;

Niels Möller's avatar
Niels Möller committed
258
	  FOR_FDS(struct listen_fd, fd, b->listen, i++)
Niels Möller's avatar
Niels Möller committed
259
	    {
Niels Möller's avatar
Niels Möller committed
260
	      if (fds[i].revents & POLLIN)
Niels Möller's avatar
Niels Möller committed
261
		{
Niels Möller's avatar
Niels Möller committed
262
263
		  /* FIXME: Do something with the peer address? */
		  struct sockaddr_in peer;
Niels Möller's avatar
Niels Möller committed
264
		  size_t addr_len = sizeof(peer);
Niels Möller's avatar
Niels Möller committed
265
		  
Niels Möller's avatar
Niels Möller committed
266
267
		  int conn = accept(fd->fd,
				    (struct sockaddr *) &peer, &addr_len);
268
269
270
271
272
273
274
275
276
		  if (conn < 0)
		    {
		      werror("io.c: accept() failed, %s", strerror(errno));
		      continue;
		    }
		  if (!FD_CALLBACK(fd->callback, conn))
		    {
		      /* FIXME: Should fd be closed here? */
		      UNLINK_FD;
Niels Möller's avatar
Niels Möller committed
277
		      lsh_free(fd);
Niels Möller's avatar
Niels Möller committed
278
		      continue;
279
		    }
Niels Möller's avatar
Niels Möller committed
280
281
		}
	    }
282
283
284
	  END_FOR_FDS;
	  
	  FOR_FDS(struct connect_fd, fd, b->connect, i++)
Niels Möller's avatar
Niels Möller committed
285
	    {
Niels Möller's avatar
Niels Möller committed
286
	      if (fds[i].revents & POLLOUT)
Niels Möller's avatar
Niels Möller committed
287
		{
288
289
		  if (!FD_CALLBACK(fd->callback, fd->fd))
		    fatal("What now?");
Niels Möller's avatar
Niels Möller committed
290
		  b->nconnect--;
291
		  UNLINK_FD;
Niels Möller's avatar
Niels Möller committed
292
		  lsh_free(fd);
Niels Möller's avatar
Niels Möller committed
293
		  continue;
Niels Möller's avatar
Niels Möller committed
294
295
		}
	    }
296
297
	  END_FOR_FDS;
	}
298
   }
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
}

/*
 * Fill in ADDR from HOST, SERVICE and PROTOCOL.
 * Supplying a null pointer for HOST means use INADDR_ANY.
 * Otherwise HOST is an numbers-and-dits ip-number or a dns name.
 *
 * PROTOCOL can be tcp or udp.
 *
 * Supplying a null pointer for SERVICE, means use port 0, i.e. no port.
 * 
 * Returns zero on errors, 1 if everything is ok.
 */
int
get_inaddr(struct sockaddr_in	* addr,
	   const char		* host,
	   const char		* service,
	   const char		* protocol)
{
  memset(addr, 0, sizeof *addr);
  addr->sin_family = AF_INET;

  /*
   *  Set host part of ADDR
   */
  if (host == NULL)
    addr->sin_addr.s_addr = INADDR_ANY;
  else
    {
      /* First check for numerical ip-number */
      addr->sin_addr.s_addr = inet_addr(host);
      if (addr->sin_addr.s_addr == (unsigned long)-1)
	{
	  struct hostent * hp;
	  
	  hp = gethostbyname(host);
	  if (hp == NULL)
	    return 0;
	  memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
	  addr->sin_family = hp->h_addrtype;
	}
    }

  /*
   *  Set port part of ADDR
   */
  if (service == NULL)
    addr->sin_port = htons(0);
  else
    {
      char		* end;
      long		  portno;

      portno = strtol(service, &end, 10);
      if (portno > 0  &&  portno <= 65535
	  &&  end != service  &&  *end == '\0')
	{
	  addr->sin_port = htons(portno);
	}
      else
	{
	  struct servent	* serv;

	  serv = getservbyname(service, "tcp");
	  if (serv == NULL)
	    return 0;
	  addr->sin_port = serv->s_port;
Niels Möller's avatar
Niels Möller committed
366
367
	}
    }
368
369

  return 1;
Niels Möller's avatar
Niels Möller committed
370
371
372
373
374
375
376
377
}

void io_set_nonblocking(int fd)
{
  if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
    fatal("io_set_nonblocking: fcntl() failed, %s", strerror(errno));
}

378
379
380
381
382
/* Some code is taken from bellman's tcputils. */
struct connect_fd *io_connect(struct io_backend *b,
			      struct sockaddr_in *remote,
			      struct sockaddr_in *local,
			      struct fd_callback *f)
Niels Möller's avatar
Niels Möller committed
383
{
384
385
  struct connect_fd *fd;
  int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Niels Möller's avatar
Niels Möller committed
386
  
387
388
  if (s<0)
    return NULL;
Niels Möller's avatar
Niels Möller committed
389

390
  io_set_nonblocking(s);
Niels Möller's avatar
Niels Möller committed
391

392
393
394
395
396
397
398
  if (local  &&  bind(s, (struct sockaddr *)local, sizeof *local) < 0)
    {
      int saved_errno = errno;
      close(s);
      errno = saved_errno;
      return NULL;
    }
Niels Möller's avatar
Niels Möller committed
399

400
401
402
403
404
405
406
407
408
409
410
  if ( (connect(s, (struct sockaddr *)remote, sizeof *remote) < 0)
       && (errno != EINPROGRESS) )       
    {
      int saved_errno = errno;
      close(s);
      errno = saved_errno;
      return NULL;
    }
  
  fd = xalloc(sizeof(struct connect_fd));
  fd->fd = s;
Niels Möller's avatar
Niels Möller committed
411
  fd->callback = f;
412
413
414

  fd->next = b->connect;
  b->connect = fd;
Niels Möller's avatar
Niels Möller committed
415

416
417
418
  b->nconnect++;
  
  return fd;
Niels Möller's avatar
Niels Möller committed
419
420
}

421
422
423
424
425
426
struct listen_fd *io_listen(struct io_backend *b,
			    struct sockaddr_in *local,
			    struct fd_callback *callback)
{
  struct listen_fd *fd;
  int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Niels Möller's avatar
Niels Möller committed
427
  
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
  if (s<0)
    return NULL;

  io_set_nonblocking(s);

  {
    int yes = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof yes);
  }

  if (bind(s, (struct sockaddr *)local, sizeof *local) < 0)
    {
      close(s);
      return NULL;
    }

  if (listen(s, 256) < 0) 
    {
      close(s);
      return NULL;
    }

  fd = xalloc(sizeof(struct connect_fd));

  fd->fd = s;
  fd->callback = callback;

  fd->next = b->listen;
  b->listen = fd;
  b->nlisten++;
Niels Möller's avatar
Niels Möller committed
458
  
459
460
461
  return fd;
}

Niels Möller's avatar
Niels Möller committed
462
463
struct abstract_write *io_read_write(struct io_backend *b,
				     int fd,
Niels Möller's avatar
Niels Möller committed
464
				     struct read_handler *read_callback,
Niels Möller's avatar
Niels Möller committed
465
466
				     UINT32 block_size,
				     struct callback *close_callback)
467
{
Niels Möller's avatar
Niels Möller committed
468
  struct io_fd *f= xalloc(sizeof(struct io_fd));
469
470
  struct write_buffer *buffer = write_buffer_alloc(block_size);
  
Niels Möller's avatar
Niels Möller committed
471
472
  f->fd = fd;
  f->please_close = 0;
Niels Möller's avatar
Niels Möller committed
473
  
Niels Möller's avatar
Niels Möller committed
474
475
476
  f->handler = read_callback;
  f->close_callback = close_callback;
  f->buffer = buffer;
477

Niels Möller's avatar
Niels Möller committed
478
479
  f->next = b->io;
  b->io = f;
Niels Möller's avatar
Niels Möller committed
480
  b->nio++;
481

482
  return &buffer->super;
483
}