Blame view

mailbox2/tcpstream.c 5.65 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.

4 5 6 7
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
8

9
   This library is distributed in the hope that it will be useful,
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11 12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
13

14 15 16
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA  */
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
34
#include <sys/time.h>
35

36
#include <mailutils/sys/tcpstream.h>
37
#include <mailutils/error.h>
38
#include <mailutils/monitor.h>
39 40 41 42 43 44

/* On solaris inet_addr() return -1.  */
#ifndef INADDR_NONE
# define INADDR_NONE (unsigned long)-1
#endif

45
static void
46
_stream_tcp_cleanup (void *arg)
47
{
48 49
  struct _stream_tcp *tcp = arg;
  mu_refcount_unlock (tcp->base.refcount);
50 51
}

52 53
void
_stream_tcp_destroy (stream_t *pstream)
54
{
55 56
  struct _stream_tcp *tcp = (struct _stream_tcp *)*pstream;
  if (mu_refcount_dec (tcp->base.refcount) == 0)
57
    {
58
      _stream_fd_dtor (*pstream);
59 60 61
      if (tcp->host)
	free (tcp->host);
      free (tcp);
62 63 64 65
    }
}

static int
66
_stream_tcp_close0 (stream_t stream)
67
{
68 69 70 71 72
  struct _stream_tcp *tcp = (struct _stream_tcp *)stream;

  tcp->base.state = MU_STREAM_STATE_CLOSE;
  if (tcp->base.fd >= 0)
    close (tcp->base.fd);
73 74 75
  if (tcp->host)
    free (tcp->host);
  tcp->host = NULL;
76 77 78 79
  tcp->state = TCP_STATE_INIT;
  return 0;
}

80 81
int
_stream_tcp_close (stream_t stream)
82
{
83
  struct _stream_tcp *tcp = (struct _stream_tcp *)stream;
84

85 86 87 88 89
  _stream_fd_close (stream);
  if (tcp->host)
    free (tcp->host);
  tcp->host = NULL;
  tcp->state = TCP_STATE_INIT;
90 91 92 93
  return 0;
}

static int
94
_stream_tcp_open0 (stream_t stream, const char *host, int port, int flags)
95
{
96
  struct _stream_tcp 	*tcp = (struct _stream_tcp *)stream;
97 98 99 100 101 102
  int flgs, ret;
  size_t namelen;
  struct sockaddr_in peer_addr;
  struct hostent *phe;
  struct sockaddr_in soc_addr;

103
  tcp->base.state = MU_STREAM_STATE_OPEN;
104 105 106
  if (tcp->state == TCP_STATE_INIT)
    {
      tcp->port = port;
107 108
      if (tcp->host)
	free (tcp->host);
109 110 111
      tcp->host = strdup (host);
      if (tcp->host == NULL)
	return MU_ERROR_NO_MEMORY;
112
      tcp->base.flags = flags;
113 114 115 116 117
    }

  switch (tcp->state)
    {
    case TCP_STATE_INIT:
118
      if (tcp->base.fd == -1)
119
	{
120 121
	  tcp->base.fd = socket (AF_INET, SOCK_STREAM, 0);
	  if (tcp->base.fd == -1)
122 123
	    return errno;
	}
124
      if (tcp->base.flags & MU_STREAM_NONBLOCK)
125
	{
126
	  flgs = fcntl (tcp->base.fd, F_GETFL);
127
	  flgs |= O_NONBLOCK;
128
	  fcntl (tcp->base.fd, F_SETFL, flgs);
129 130 131 132 133 134 135 136 137 138 139 140
	}
      tcp->state = TCP_STATE_RESOLVING;

    case TCP_STATE_RESOLVING:
      if (tcp->host == NULL || tcp->port == -1)
	return MU_ERROR_INVALID_PARAMETER;
      tcp->address = inet_addr (tcp->host);
      if (tcp->address == INADDR_NONE)
	{
	  phe = gethostbyname (tcp->host);
	  if (!phe)
	    {
141
	      _stream_tcp_close0 (stream);
142 143 144 145 146 147 148
	      return MU_ERROR_INVALID_PARAMETER;
	    }
	  tcp->address = *(((unsigned long **)phe->h_addr_list)[0]);
	}
      tcp->state = TCP_STATE_RESOLVE;

    case TCP_STATE_RESOLVE:
149
      memset (&soc_addr, 0, sizeof soc_addr);
150 151 152 153
      soc_addr.sin_family = AF_INET;
      soc_addr.sin_port = htons (tcp->port);
      soc_addr.sin_addr.s_addr = tcp->address;

154 155
      if ((connect (tcp->base.fd, (struct sockaddr *)&soc_addr,
		    sizeof soc_addr)) == -1)
156 157 158 159 160 161 162 163
	{
	  ret = errno;
	  if (ret == EINPROGRESS || ret == EAGAIN)
	    {
	      tcp->state = TCP_STATE_CONNECTING;
	      ret = MU_ERROR_TRY_AGAIN;
	    }
	  else
164
	    _stream_tcp_close0 (stream);
165 166 167 168 169
	  return ret;
	}
      tcp->state = TCP_STATE_CONNECTING;

    case TCP_STATE_CONNECTING:
170
      namelen = sizeof peer_addr;
171 172
      if (getpeername (tcp->base.fd, (struct sockaddr *)&peer_addr,
		       &namelen) == 0)
173 174 175 176
	tcp->state = TCP_STATE_CONNECTED;
      else
	{
	  ret = errno;
177
	  _stream_tcp_close0 (stream);
178 179 180 181 182 183 184
	  return ret;
	}
      break;
    }
  return 0;
}

185 186
int
_stream_tcp_open (stream_t stream, const char *host, int port, int flags)
187 188
{
  int status;
189 190 191 192 193
  struct _stream_tcp *tcp = (struct _stream_tcp *)stream;
  mu_refcount_lock (tcp->base.refcount);
  monitor_cleanup_push (_stream_tcp_cleanup, tcp);
  status = _stream_tcp_open0 (stream, host, port, flags);
  mu_refcount_unlock (tcp->base.refcount);
194 195 196 197
  monitor_cleanup_pop (0);
  return status;
}

198
static struct _stream_vtable _stream_tcp_vtable;
199 200 201 202

int
stream_tcp_create (stream_t *pstream)
{
203 204 205
  struct _stream_tcp *tcp;
  stream_t stream;
  int status;
206

207 208 209
  if (pstream == NULL)
    return MU_ERROR_INVALID_PARAMETER;

210 211
  tcp = calloc (1, sizeof *tcp);
  if (tcp == NULL)
212
    {
213
      stream_destroy (&stream);
214 215
      return MU_ERROR_NO_MEMORY;
    }
216 217 218 219 220 221

  /* Create the base.  */
  status = _stream_fd_ctor (&tcp->base, -1);
  if (status != 0)
    return status;

222 223 224
  tcp->host = NULL;
  tcp->port = -1;
  tcp->state = TCP_STATE_INIT;
225 226 227 228 229 230 231 232
  _stream_tcp_vtable = *(tcp->base.base.vtable);
  /* Overload.  */
  _stream_tcp_vtable.open = _stream_tcp_open;
  _stream_tcp_vtable.close = _stream_tcp_close;
  _stream_tcp_vtable.destroy = _stream_tcp_destroy;

  tcp->base.base.vtable = &_stream_tcp_vtable;
  *pstream = &tcp->base.base;
233 234
  return 0;
}