Blame view

mailbox/tcp.c 7.75 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 1999, 2000, 2004, 2006, 2007, 2010 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
8
   version 3 of the License, or (at your option) any later version.
9

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

15 16 17 18
   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., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA */
19

20 21 22 23
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

24 25
#include <errno.h>
#include <fcntl.h>
26 27 28
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
29
#include <string.h>
30 31
#include <unistd.h>

32
#include <sys/socket.h>
33 34
#include <sys/types.h>

35
#include <netinet/in.h>
36

37 38
#include <arpa/inet.h>

39
#include <mailutils/errno.h>
40
#include <mailutils/stream.h>
41
#include <mailutils/mutil.h>
42

43 44
#include <mailutils/sys/stream.h>

45 46 47 48 49 50
#define TCP_STATE_INIT 		1
#define TCP_STATE_RESOLVE	2
#define TCP_STATE_RESOLVING	3
#define TCP_STATE_CONNECTING 	4
#define TCP_STATE_CONNECTED	5

51 52
struct _tcp_instance
{
53
  struct _mu_stream stream;
54 55 56 57 58 59
  int 		fd;
  char 		*host;
  int 		port;
  int		state;
  unsigned long	address;
  unsigned long source_addr;
60
};
61

62 63 64 65 66
/* On solaris inet_addr() return -1.  */
#ifndef INADDR_NONE
# define INADDR_NONE (unsigned long)-1
#endif

67
static int
68
_tcp_close (mu_stream_t stream)
69
{
70
  struct _tcp_instance *tcp = (struct _tcp_instance *)stream;
71
  int err = 0;
72

73
  if (tcp->fd != -1)
74 75 76 77 78 79
    {
      if (close (tcp->fd) != 0)
	{
	  err = errno;
	}
    }
80 81
  tcp->fd = -1;
  tcp->state = TCP_STATE_INIT;
82
  return err;
83 84
}

85
static int
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
resolve_hostname (const char *host, unsigned long *ip)
{
  unsigned long address = inet_addr (host);
  if (address == INADDR_NONE)
    {
      struct hostent *phe = gethostbyname (host);
      if (!phe)
	return MU_ERR_GETHOSTBYNAME;
      address = *(((unsigned long **) phe->h_addr_list)[0]);
    }
  *ip = address;
  return 0;
}

static int
101
_tcp_open (mu_stream_t stream)
102
{
103
  struct _tcp_instance *tcp = (struct _tcp_instance *)stream;
104
  int flgs, ret;
105
  socklen_t namelen;
106 107
  struct sockaddr_in peer_addr;
  struct sockaddr_in soc_addr;
108 109
  int flags;

110
  mu_stream_get_flags (stream, &flags);
111 112 113 114 115 116

  switch (tcp->state)
    {
    case TCP_STATE_INIT:
      if (tcp->fd == -1)
	{
117
	  if ((tcp->fd = socket (PF_INET, SOCK_STREAM, 0)) == -1)
118
	    return errno;
119
	}
120 121 122 123 124
      if (flags & MU_STREAM_NONBLOCK)
	{
	  flgs = fcntl (tcp->fd, F_GETFL);
	  flgs |= O_NONBLOCK;
	  fcntl (tcp->fd, F_SETFL, flgs);
125
	  mu_stream_set_flags (stream, MU_STREAM_NONBLOCK);
126
	}
127
      if (tcp->source_addr != INADDR_ANY)
128
	{
129 130 131 132 133
	  struct sockaddr_in s;
	  s.sin_family = AF_INET;
	  s.sin_addr.s_addr = tcp->source_addr;
	  s.sin_port = 0;
	  if (bind (tcp->fd, (struct sockaddr*) &s, sizeof(s)) < 0)
134
	    {
135 136 137 138
	      int e = errno;
	      close (tcp->fd);
	      tcp->fd = -1;
	      return e;
139
	    }
140 141 142
	}
      
      tcp->state = TCP_STATE_RESOLVING;
143
      
144 145 146 147 148 149 150 151 152 153 154
    case TCP_STATE_RESOLVING:
      if (!(tcp->host != NULL && tcp->port > 0))
	{
	  _tcp_close (stream);
	  return EINVAL;
	}
      
      if ((ret = resolve_hostname (tcp->host, &tcp->address)))
	{
	  _tcp_close (stream);
	  return ret;
155 156
	}
      tcp->state = TCP_STATE_RESOLVE;
157
      
158 159 160 161 162 163 164
    case TCP_STATE_RESOLVE:
      memset (&soc_addr, 0, sizeof (soc_addr));
      soc_addr.sin_family = AF_INET;
      soc_addr.sin_port = htons (tcp->port);
      soc_addr.sin_addr.s_addr = tcp->address;

      if ((connect (tcp->fd,
165
		    (struct sockaddr *) &soc_addr, sizeof (soc_addr))) == -1)
166 167 168 169 170 171 172 173 174 175
	{
	  ret = errno;
	  if (ret == EINPROGRESS || ret == EAGAIN)
	    {
	      tcp->state = TCP_STATE_CONNECTING;
	      ret = EAGAIN;
	    }
	  else
	    _tcp_close (stream);
	  return ret;
176
	}
177
      tcp->state = TCP_STATE_CONNECTING;
178
      
179 180 181
    case TCP_STATE_CONNECTING:
      namelen = sizeof (peer_addr);
      if (getpeername (tcp->fd,
182
		       (struct sockaddr *) &peer_addr, &namelen) == 0)
183 184 185 186 187 188 189 190 191 192
	tcp->state = TCP_STATE_CONNECTED;
      else
	{
	  ret = errno;
	  _tcp_close (stream);
	  return ret;
	}
      break;
    }
  return 0;
193 194
}

195
static int
196
_tcp_ioctl (mu_stream_t stream, int code, void *ptr)
197
{
198 199
  struct _tcp_instance *tcp = (struct _tcp_instance *)stream;
  mu_transport_t (*ptrans)[2];
200

201 202 203 204 205 206 207 208 209
  switch (code)
    {
    case MU_IOCTL_GET_TRANSPORT:
      if (!ptr)
	return EINVAL;
      ptrans = ptr;
      (*ptrans)[0] = (mu_transport_t) tcp->fd;
      (*ptrans)[1] = NULL;
      break;
210

211 212 213
    default:
      return EINVAL;
    }
214
  return 0;
215 216
}

217
static int
218
_tcp_read (mu_stream_t stream, char *buf, size_t size, size_t *pret)
219
{
220 221 222 223 224 225
  struct _tcp_instance *tcp = (struct _tcp_instance *)stream;
  ssize_t bytes;

  if ((bytes = recv (tcp->fd, buf, size, 0)) == -1)
    return errno;
  *pret = bytes;
226
  return 0;
227 228
}

229
static int
230
_tcp_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret)
231
{
232 233 234 235 236 237
  struct _tcp_instance *tcp = (struct _tcp_instance *)stream;
  ssize_t bytes;

  if ((bytes = send (tcp->fd, buf, size, 0)) == -1)
    return errno;
  *pret = bytes;
238
  return 0;
239 240
}

241
static void
242
_tcp_done (mu_stream_t stream)
243
{
244
  struct _tcp_instance *tcp = (struct _tcp_instance *)stream;
245

246 247 248 249
  if (tcp->host)
    free (tcp->host);
  if (tcp->fd != -1)
    close (tcp->fd);
250 251
}

252
int
253
_tcp_wait (mu_stream_t stream, int *pflags, struct timeval *tvp)
254
{
255
  struct _tcp_instance *tcp = (struct _tcp_instance *)stream;
256 257 258 259 260 261
  if (tcp->fd == -1)
    return EINVAL;
  return mu_fd_wait (tcp->fd, pflags, tvp);
}

int
262 263
_tcp_shutdown (mu_stream_t stream, int how)
{
264
  struct _tcp_instance *tcp = (struct _tcp_instance *)stream;
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
  int flag;
  if (tcp->fd == -1)
    return EINVAL;

  switch (how)
    {
    case MU_STREAM_READ:
      flag = SHUT_RD;
      break;
      
    case MU_STREAM_WRITE:
      flag = SHUT_WR;
    }

  if (shutdown (tcp->fd, flag))
    return errno;
  return 0;
}

284 285
static struct _tcp_instance *
_create_tcp_stream (int flags)
286
{
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
  struct _tcp_instance *tcp =
    (struct _tcp_instance *)_mu_stream_create (sizeof (*tcp), flags);

  if (tcp)
    {
      tcp->stream.open = _tcp_open;
      tcp->stream.close = _tcp_close;
      tcp->stream.read = _tcp_read;
      tcp->stream.write = _tcp_write;
      tcp->stream.ctl = _tcp_ioctl;
      tcp->stream.done = _tcp_done;
      tcp->stream.wait = _tcp_wait;
      tcp->stream.shutdown = _tcp_shutdown;
      tcp->fd = -1;
      tcp->state = TCP_STATE_INIT;
    }
  return tcp;
304 305 306
}

int
307 308 309 310
mu_tcp_stream_create_with_source_ip (mu_stream_t *stream,
				     const char *host, int port,
				     unsigned long source_ip,
				     int flags)
311
{
312 313
  struct _tcp_instance *tcp;

314
  if (host == NULL)
315
    return MU_ERR_TCP_NO_HOST;
316 317 318

  if (port < 1)
    return MU_ERR_TCP_NO_PORT;
319

320 321
  tcp = _create_tcp_stream (flags | MU_STREAM_NO_CHECK | MU_STREAM_RDWR);
  if (!tcp)
322
    return ENOMEM;
323
  tcp->host = strdup (host);
324
  if (!tcp->host)
325 326 327 328
    {
      free (tcp);
      return ENOMEM;
    }
329
  tcp->port = port;
330
  tcp->state = TCP_STATE_INIT;
331
  tcp->source_addr = source_ip;
332
  *stream = (mu_stream_t) tcp;
333
  return 0;
334
}
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356

int
mu_tcp_stream_create_with_source_host (mu_stream_t *stream,
				       const char *host, int port,
				       const char *source_host,
				       int flags)
{
  unsigned long source_addr;
  int ret = resolve_hostname (source_host, &source_addr);
  if (ret == 0)
    ret = mu_tcp_stream_create_with_source_ip (stream, host, port,
					       source_addr, flags);
  return ret;
}
       
int
mu_tcp_stream_create (mu_stream_t *stream, const char *host, int port,
		      int flags)
{
  return mu_tcp_stream_create_with_source_ip (stream, host, port,
					      INADDR_ANY, flags);
}