diff options
Diffstat (limited to 'libmailutils/stream/tcp.c')
-rw-r--r-- | libmailutils/stream/tcp.c | 178 |
1 files changed, 96 insertions, 82 deletions
diff --git a/libmailutils/stream/tcp.c b/libmailutils/stream/tcp.c index 40b37124d..f383f1b5a 100644 --- a/libmailutils/stream/tcp.c +++ b/libmailutils/stream/tcp.c @@ -36,37 +36,30 @@ #include <arpa/inet.h> #include <mailutils/errno.h> #include <mailutils/stream.h> #include <mailutils/util.h> - +#include <mailutils/sockaddr.h> #include <mailutils/sys/stream.h> #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 struct _tcp_instance { struct _mu_stream stream; int fd; - char *host; - unsigned short port; int state; - unsigned long address; - unsigned long source_addr; + struct mu_sockaddr *remote_addr; + struct mu_sockaddr *source_addr; }; -/* On solaris inet_addr() return -1. */ -#ifndef INADDR_NONE -# define INADDR_NONE (unsigned long)-1 -#endif - static int _tcp_close (mu_stream_t stream) { struct _tcp_instance *tcp = (struct _tcp_instance *)stream; int err = 0; @@ -80,92 +73,58 @@ _tcp_close (mu_stream_t stream) tcp->fd = -1; tcp->state = TCP_STATE_INIT; return err; } static int -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 _tcp_open (mu_stream_t stream) { struct _tcp_instance *tcp = (struct _tcp_instance *)stream; int flgs, ret; socklen_t namelen; struct sockaddr_in peer_addr; - struct sockaddr_in soc_addr; int flags; mu_stream_get_flags (stream, &flags); switch (tcp->state) { case TCP_STATE_INIT: if (tcp->fd == -1) { - if ((tcp->fd = socket (PF_INET, SOCK_STREAM, 0)) == -1) + tcp->fd = socket (tcp->remote_addr->addr->sa_family, SOCK_STREAM, 0); + if (tcp->fd == -1) return errno; } if (flags & MU_STREAM_NONBLOCK) { flgs = fcntl (tcp->fd, F_GETFL); flgs |= O_NONBLOCK; fcntl (tcp->fd, F_SETFL, flgs); mu_stream_set_flags (stream, MU_STREAM_NONBLOCK); } - if (tcp->source_addr != INADDR_ANY) + if (tcp->source_addr) { - 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) + if (bind (tcp->fd, tcp->source_addr->addr, + tcp->source_addr->addrlen) < 0) { int e = errno; close (tcp->fd); tcp->fd = -1; return e; } } tcp->state = TCP_STATE_RESOLVING; 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; - } tcp->state = TCP_STATE_RESOLVE; 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, - (struct sockaddr *) &soc_addr, sizeof (soc_addr))) == -1) + if (connect (tcp->fd, tcp->remote_addr->addr, + tcp->remote_addr->addrlen) == -1) { ret = errno; if (ret == EINPROGRESS || ret == EAGAIN) { tcp->state = TCP_STATE_CONNECTING; ret = EAGAIN; @@ -251,16 +210,14 @@ _tcp_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret) static void _tcp_done (mu_stream_t stream) { struct _tcp_instance *tcp = (struct _tcp_instance *)stream; - if (tcp->host) - free (tcp->host); - if (tcp->fd != -1) - close (tcp->fd); + mu_sockaddr_free (tcp->remote_addr); + mu_sockaddr_free (tcp->source_addr); } int _tcp_wait (mu_stream_t stream, int *pflags, struct timeval *tvp) { struct _tcp_instance *tcp = (struct _tcp_instance *)stream; @@ -312,63 +269,120 @@ _create_tcp_stream (int flags) tcp->state = TCP_STATE_INIT; } return tcp; } int -mu_tcp_stream_create_with_source_ip (mu_stream_t *pstream, - const char *host, unsigned port, - unsigned long source_ip, - int flags) +mu_tcp_stream_create_from_sa (mu_stream_t *pstream, + struct mu_sockaddr *remote_addr, + struct mu_sockaddr *source_addr, int flags) { int rc; mu_stream_t stream; struct _tcp_instance *tcp; - if (host == NULL) - return MU_ERR_TCP_NO_HOST; - - if (port > USHRT_MAX) - return MU_ERR_TCP_NO_PORT; - tcp = _create_tcp_stream (flags | MU_STREAM_RDWR); if (!tcp) return ENOMEM; - tcp->host = strdup (host); - if (!tcp->host) - { - free (tcp); - return ENOMEM; - } - tcp->port = port; - tcp->state = TCP_STATE_INIT; - tcp->source_addr = source_ip; + + tcp->remote_addr = remote_addr; + tcp->source_addr = source_addr; + stream = (mu_stream_t) tcp; rc = mu_stream_open (stream); if (rc == 0 || rc == EAGAIN || rc == EINPROGRESS) *pstream = stream; else mu_stream_destroy (&stream); return rc; } + +int +mu_tcp_stream_create_with_source_ip (mu_stream_t *pstream, + const char *host, unsigned port, + unsigned long source_ip, + int flags) +{ + int rc; + struct mu_sockaddr *remote_addr, *source_addr = NULL; + struct mu_sockaddr_hints hints; + + memset (&hints, 0, sizeof hints); + hints.family = AF_INET; + hints.socktype = SOCK_STREAM; + hints.protocol = IPPROTO_TCP; + hints.port = port; + rc = mu_sockaddr_from_node (&remote_addr, host, NULL, &hints); + if (rc) + return rc; + + if (source_ip) + { + struct sockaddr_in s; + s.sin_family = AF_INET; + s.sin_addr.s_addr = source_ip; + s.sin_port = 0; + rc = mu_sockaddr_create (&source_addr, (struct sockaddr*)&s, + sizeof (s)); + if (rc) + { + mu_sockaddr_free (remote_addr); + return 0; + } + } + + rc = mu_tcp_stream_create_from_sa (pstream, remote_addr, source_addr, flags); + if (rc) + { + mu_sockaddr_free (remote_addr); + mu_sockaddr_free (source_addr); + } + return rc; +} + int mu_tcp_stream_create_with_source_host (mu_stream_t *stream, const char *host, unsigned 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 rc; + struct mu_sockaddr *remote_addr, *source_addr = NULL; + struct mu_sockaddr_hints hints; + + memset (&hints, 0, sizeof hints); + hints.family = AF_INET; + hints.socktype = SOCK_STREAM; + hints.port = port; + rc = mu_sockaddr_from_node (&remote_addr, host, NULL, &hints); + if (rc) + return rc; + + if (source_host) + { + hints.flags = MU_AH_PASSIVE; + hints.port = 0; + rc = mu_sockaddr_from_node (&source_addr, source_host, NULL, &hints); + if (rc) + { + mu_sockaddr_free (remote_addr); + return 0; + } + } + + rc = mu_tcp_stream_create_from_sa (stream, remote_addr, source_addr, flags); + if (rc) + { + mu_sockaddr_free (remote_addr); + mu_sockaddr_free (source_addr); + } + return rc; } int mu_tcp_stream_create (mu_stream_t *stream, const char *host, unsigned port, int flags) { - return mu_tcp_stream_create_with_source_ip (stream, host, port, - INADDR_ANY, flags); + return mu_tcp_stream_create_with_source_host (stream, host, port, + NULL, flags); } |