io.c 9.87 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 26 27 28
 */

#include <unistd.h>
#include <poll.h>
#include <errno.h>
Niels Möller's avatar
Niels Möller committed
29
#include <string.h>
Niels Möller's avatar
Niels Möller committed
30 31 32 33

#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
Niels Möller's avatar
Niels Möller committed
34
#include <arpa/inet.h>
Niels Möller's avatar
Niels Möller committed
35

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

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

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

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

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

63 64 65 66 67 68 69
      switch(errno)
	{
	case EINTR:
	  continue;
	case EWOULDBLOCK:  /* aka EAGAIN */
	  return 0;
	default:
Niels Möller's avatar
Niels Möller committed
70
	  werror("io.c: do_read: read() failed, %s\n", strerror(errno));
71 72 73 74
	  return A_FAIL;
	}
    }
}
Niels Möller's avatar
Niels Möller committed
75

76 77 78
#define FOR_FDS(type, fd, list, extra)				\
{								\
  type **(_fd);							\
Niels Möller's avatar
Niels Möller committed
79
  type *(fd);							\
Niels Möller's avatar
Niels Möller committed
80
  for(_fd = &(list); ((fd) = *_fd); (extra)) {
81 82


Niels Möller's avatar
Niels Möller committed
83 84 85
#define END_FOR_FDS _fd = &(*_fd)->next; } }

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

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

Niels Möller's avatar
Niels Möller committed
98
      nfds = b->nio + b->nlisten + b->nconnect;
Niels Möller's avatar
Niels Möller committed
99 100

      if (b->callouts)
101 102 103 104 105 106 107 108 109 110 111 112
	 {
	   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
113
      else
114 115 116 117 118 119 120
	 {
	   if (!nfds)
	     /* All done */
	     break;
	   timeout = -1;
	 }

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

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

126
      FOR_FDS(struct io_fd, fd, b->io, i++)
127 128 129 130 131 132 133 134 135 136
	 {
	   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;
	 }
137 138
      END_FOR_FDS;

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

      FOR_FDS(struct connect_fd, fd, b->connect, i++)
147 148 149 150
	 {
	   fds[i].fd = fd->fd;
	   fds[i].events = POLLOUT;
	 }
151 152
      END_FOR_FDS;

Niels Möller's avatar
Niels Möller committed
153 154 155
      res = poll(fds, nfds, timeout);

      if (!res)
156 157 158 159 160 161 162
	 {
	   /* 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
163
	   lsh_free(f);
164
	 }
Niels Möller's avatar
Niels Möller committed
165
      if (res<0)
166 167 168 169 170 171 172 173 174 175
	 {
	   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
176
      else
177 178 179 180 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
	 { /* 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 };

224 225 226
		   /* The handler function may install a new handler */
		   if (!READ_HANDLER(fd->handler,
				     &r.super))
227
		     {
228 229 230 231 232 233
		       /* 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. */
234 235
		       fd->please_close = 1;
		     }
236
		}
Niels Möller's avatar
Niels Möller committed
237 238 239 240 241 242 243
	      if (fd->please_close)
		{
		  /* FIXME: Cleanup properly...
		   *
		   * After a write error, read state must be freed,
		   * and vice versa. */
		  UNLINK_FD;
244 245
		  b->nio--;
		  if (fd->handler)
Niels Möller's avatar
Niels Möller committed
246 247 248
		    lsh_free(fd->handler);
		  lsh_free(fd->buffer);
		  lsh_free(fd);
Niels Möller's avatar
Niels Möller committed
249
		  continue;
Niels Möller's avatar
Niels Möller committed
250
		}
251 252 253
	    }
	  END_FOR_FDS;

Niels Möller's avatar
Niels Möller committed
254
	  FOR_FDS(struct listen_fd, fd, b->listen, i++)
Niels Möller's avatar
Niels Möller committed
255
	    {
Niels Möller's avatar
Niels Möller committed
256
	      if (fds[i].revents & POLLIN)
Niels Möller's avatar
Niels Möller committed
257
		{
Niels Möller's avatar
Niels Möller committed
258 259
		  /* FIXME: Do something with the peer address? */
		  struct sockaddr_in peer;
Niels Möller's avatar
Niels Möller committed
260
		  size_t addr_len = sizeof(peer);
Niels Möller's avatar
Niels Möller committed
261
		  
Niels Möller's avatar
Niels Möller committed
262 263
		  int conn = accept(fd->fd,
				    (struct sockaddr *) &peer, &addr_len);
264 265 266 267 268 269 270 271 272
		  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
273
		      lsh_free(fd);
Niels Möller's avatar
Niels Möller committed
274
		      continue;
275
		    }
Niels Möller's avatar
Niels Möller committed
276 277
		}
	    }
278 279 280
	  END_FOR_FDS;
	  
	  FOR_FDS(struct connect_fd, fd, b->connect, i++)
Niels Möller's avatar
Niels Möller committed
281
	    {
Niels Möller's avatar
Niels Möller committed
282
	      if (fds[i].revents & POLLOUT)
Niels Möller's avatar
Niels Möller committed
283
		{
284 285
		  if (!FD_CALLBACK(fd->callback, fd->fd))
		    fatal("What now?");
Niels Möller's avatar
Niels Möller committed
286
		  b->nconnect--;
287
		  UNLINK_FD;
Niels Möller's avatar
Niels Möller committed
288
		  lsh_free(fd);
Niels Möller's avatar
Niels Möller committed
289
		  continue;
Niels Möller's avatar
Niels Möller committed
290 291
		}
	    }
292 293
	  END_FOR_FDS;
	}
294
   }
295 296 297 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
}

/*
 * 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
362 363
	}
    }
364 365

  return 1;
Niels Möller's avatar
Niels Möller committed
366 367 368 369 370 371 372 373
}

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

374 375 376 377 378
/* 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
379
{
380 381
  struct connect_fd *fd;
  int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Niels Möller's avatar
Niels Möller committed
382
  
383 384
  if (s<0)
    return NULL;
Niels Möller's avatar
Niels Möller committed
385

386
  io_set_nonblocking(s);
Niels Möller's avatar
Niels Möller committed
387

388 389 390 391 392 393 394
  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
395

396 397 398 399 400 401 402 403 404 405 406
  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
407
  fd->callback = f;
408 409 410

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

412 413 414
  b->nconnect++;
  
  return fd;
Niels Möller's avatar
Niels Möller committed
415 416
}

417 418 419 420 421 422
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
423
  
424 425 426 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
  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
454
  
455 456 457
  return fd;
}

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

Niels Möller's avatar
Niels Möller committed
474 475
  f->next = b->io;
  b->io = f;
Niels Möller's avatar
Niels Möller committed
476
  b->nio++;
477

478
  return &buffer->super;
479
}