network.c revision 0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 1999-2003 Timo Sirainen */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "lib.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "network.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include <unistd.h>
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include <fcntl.h>
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include <ctype.h>
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include <sys/un.h>
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include <netinet/tcp.h>
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#define LISTEN_BACKLOG 8
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenunion sockaddr_union {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct sockaddr sa;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct sockaddr_in sin;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#ifdef HAVE_IPV6
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct sockaddr_in6 sin6;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#endif
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen};
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#ifdef HAVE_IPV6
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen sizeof(so.sin6) : sizeof(so.sin))
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#else
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen# define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#endif
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenint net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (ip1->family != ip2->family)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return 0;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#ifdef HAVE_IPV6
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (ip1->family == AF_INET6)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#endif
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return memcmp(&ip1->ip, &ip2->ip, 4) == 0;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen/* copy IP to sockaddr */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenstatic inline void
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainensin_set_ip(union sockaddr_union *so, const struct ip_addr *ip)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (ip == NULL) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#ifdef HAVE_IPV6
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen so->sin6.sin6_family = AF_INET6;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen so->sin6.sin6_addr = in6addr_any;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#else
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen so->sin.sin_family = AF_INET;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen so->sin.sin_addr.s_addr = INADDR_ANY;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#endif
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen return;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen }
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen so->sin.sin_family = ip->family;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen#ifdef HAVE_IPV6
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (ip->family == AF_INET6)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen else
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#endif
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen memcpy(&so->sin.sin_addr, &ip->ip, 4);
1d0f568e26ce5cbf18cd7bb335c6eea20a7e3770Teemu Huovila}
db090e2a48bcc5ce162af0c36eef04407421379dTeemu Huovila
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenstatic inline void
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainensin_get_ip(const union sockaddr_union *so, struct ip_addr *ip)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen ip->family = so->sin.sin_family;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#ifdef HAVE_IPV6
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (ip->family == AF_INET6)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen else
db090e2a48bcc5ce162af0c36eef04407421379dTeemu Huovila#endif
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen memcpy(&ip->ip, &so->sin.sin_addr, 4);
db090e2a48bcc5ce162af0c36eef04407421379dTeemu Huovila}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenstatic inline void sin_set_port(union sockaddr_union *so, unsigned int port)
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen{
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen#ifdef HAVE_IPV6
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen if (so->sin.sin_family == AF_INET6)
513b045d3cb2325250e74f0a92c144f9307eee44Timo Sirainen so->sin6.sin6_port = htons((unsigned short) port);
513b045d3cb2325250e74f0a92c144f9307eee44Timo Sirainen else
513b045d3cb2325250e74f0a92c144f9307eee44Timo Sirainen#endif
513b045d3cb2325250e74f0a92c144f9307eee44Timo Sirainen so->sin.sin_port = htons((unsigned short) port);
513b045d3cb2325250e74f0a92c144f9307eee44Timo Sirainen}
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenstatic inline unsigned int sin_get_port(union sockaddr_union *so)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#ifdef HAVE_IPV6
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (so->sin.sin_family == AF_INET6)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return ntohs(so->sin6.sin6_port);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#endif
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return ntohs(so->sin.sin_port);
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenstatic inline void close_save_errno(int fd)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen{
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen int old_errno = errno;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen (void)close(fd);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen errno = old_errno;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen}
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen/* Connect to socket with ip address */
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainenint net_connect_ip(const struct ip_addr *ip, unsigned int port,
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen const struct ip_addr *my_ip)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen{
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen union sockaddr_union so;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen int fd, ret, opt = 1;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen if (my_ip != NULL && ip->family != my_ip->family) {
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen i_warning("net_connect_ip(): ip->family != my_ip->family");
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen my_ip = NULL;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen }
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen /* create the socket */
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen memset(&so, 0, sizeof(so));
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen so.sin.sin_family = ip->family;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen fd = socket(ip->family, SOCK_STREAM, 0);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (fd == -1)
c5b642067ab1fb48b3f07a0a3fd08abe6aacd611Timo Sirainen return -1;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen /* set socket options */
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen /* set our own address */
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (my_ip != NULL) {
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen sin_set_ip(&so, my_ip);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen /* failed, set it back to INADDR_ANY */
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen sin_set_ip(&so, NULL);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen bind(fd, &so.sa, SIZEOF_SOCKADDR(so));
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen }
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen }
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen /* connect */
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen sin_set_ip(&so, ip);
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen sin_set_port(&so, port);
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so));
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen#ifndef WIN32
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (ret < 0 && errno != EINPROGRESS)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen#else
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen#endif
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen close_save_errno(fd);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen return -1;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen }
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return fd;
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen}
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainenint net_connect_unix(const char *path)
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen{
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen struct sockaddr_un sa;
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen int fd, ret;
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen memset(&sa, 0, sizeof(sa));
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen sa.sun_family = AF_UNIX;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen if (strocpy(sa.sun_path, path, sizeof(sa.sun_path)) < 0) {
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen /* too long path */
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen errno = EINVAL;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen return -1;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen /* create the socket */
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen fd = socket(PF_UNIX, SOCK_STREAM, 0);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (fd == -1)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen return -1;
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen /* connect */
8b1a9a4d63b0abccdf7cb1acb8359d5396dd657bTimo Sirainen ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
aaed9e3ce98759e0cb1258fc14e1076b71791445Timo Sirainen if (ret < 0 && errno != EINPROGRESS) {
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen close_save_errno(fd);
aaed9e3ce98759e0cb1258fc14e1076b71791445Timo Sirainen return -1;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen }
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen
aaed9e3ce98759e0cb1258fc14e1076b71791445Timo Sirainen return fd;
23b586b2cf5760527529f9963c04875c8566a24dTimo Sirainen}
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen/* Disconnect socket */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenvoid net_disconnect(int fd)
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (close(fd) < 0)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_error("net_disconnect() failed: %m");
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen/* Set socket blocking/nonblocking */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenvoid net_set_nonblock(int fd __attr_unused__, int nonblock __attr_unused__)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#ifdef HAVE_FCNTL
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen int flags;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen flags = fcntl(fd, F_GETFL, 0);
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen if (flags == -1)
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen i_fatal("net_set_nonblock() failed: %m");
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (nonblock)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen flags |= O_NONBLOCK;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen else
5ed7a7fd838ba316cee9c59244d263227eb2b0d8Timo Sirainen flags &= ~O_NONBLOCK;
5ed7a7fd838ba316cee9c59244d263227eb2b0d8Timo Sirainen
5ed7a7fd838ba316cee9c59244d263227eb2b0d8Timo Sirainen if (fcntl(fd, F_SETFL, flags) < 0)
5ed7a7fd838ba316cee9c59244d263227eb2b0d8Timo Sirainen i_fatal("net_set_nonblock() failed: %m");
5ed7a7fd838ba316cee9c59244d263227eb2b0d8Timo Sirainen#endif
f784d5bb8edbec88829524135cfa100129f5384dTimo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenint net_set_cork(int fd __attr_unused__, int cork __attr_unused__)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen#ifdef TCP_CORK
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen#else
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen errno = ENOPROTOOPT;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return -1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#endif
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenvoid net_get_ip_any4(struct ip_addr *ip)
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct in_addr *in_ip = (struct in_addr *) &ip->ip;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen ip->family = AF_INET;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen in_ip->s_addr = INADDR_ANY;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
6eca434b47b7b700f7df80a0e1ce31d0fd45d1fdTimo Sirainen
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainenvoid net_get_ip_any6(struct ip_addr *ip)
44ca7644e6df9e5ce7e7d0cc3767f63153c10bd7Timo Sirainen{
44ca7644e6df9e5ce7e7d0cc3767f63153c10bd7Timo Sirainen#ifdef HAVE_IPV6
44ca7644e6df9e5ce7e7d0cc3767f63153c10bd7Timo Sirainen ip->family = AF_INET6;
44ca7644e6df9e5ce7e7d0cc3767f63153c10bd7Timo Sirainen ip->ip = in6addr_any;
44ca7644e6df9e5ce7e7d0cc3767f63153c10bd7Timo Sirainen#else
44ca7644e6df9e5ce7e7d0cc3767f63153c10bd7Timo Sirainen memset(ip, 0, sizeof(struct ip_addr));
44ca7644e6df9e5ce7e7d0cc3767f63153c10bd7Timo Sirainen#endif
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen}
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen/* Listen for connections on a socket. if `my_ip' is NULL, listen in any
6eca434b47b7b700f7df80a0e1ce31d0fd45d1fdTimo Sirainen address. */
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainenint net_listen(const struct ip_addr *my_ip, unsigned int *port)
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen union sockaddr_union so;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen int ret, fd, opt = 1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen socklen_t len;
98fe03ecad7fcf0973584b9ab0b4dc4848881d56Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_assert(port != NULL);
6eca434b47b7b700f7df80a0e1ce31d0fd45d1fdTimo Sirainen
6eca434b47b7b700f7df80a0e1ce31d0fd45d1fdTimo Sirainen memset(&so, 0, sizeof(so));
6eca434b47b7b700f7df80a0e1ce31d0fd45d1fdTimo Sirainen sin_set_port(&so, *port);
6eca434b47b7b700f7df80a0e1ce31d0fd45d1fdTimo Sirainen sin_set_ip(&so, my_ip);
6eca434b47b7b700f7df80a0e1ce31d0fd45d1fdTimo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* create the socket */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen fd = socket(so.sin.sin_family, SOCK_STREAM, 0);
#ifdef HAVE_IPV6
if (fd == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
/* IPv6 is not supported by OS */
so.sin.sin_family = AF_INET;
so.sin.sin_addr.s_addr = INADDR_ANY;
fd = socket(AF_INET, SOCK_STREAM, 0);
}
#endif
if (fd == -1)
return -1;
/* set socket options */
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
/* specify the address/port we want to listen in */
ret = bind(fd, &so.sa, SIZEOF_SOCKADDR(so));
if (ret >= 0) {
/* get the actual port we started listen */
len = SIZEOF_SOCKADDR(so);
ret = getsockname(fd, &so.sa, &len);
if (ret >= 0) {
*port = sin_get_port(&so);
/* start listening */
if (listen(fd, LISTEN_BACKLOG) >= 0)
return fd;
}
}
/* error */
close_save_errno(fd);
return -1;
}
int net_listen_unix(const char *path)
{
struct sockaddr_un sa;
int fd;
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
if (strocpy(sa.sun_path, path, sizeof(sa.sun_path)) < 0) {
/* too long path */
errno = EINVAL;
return -1;
}
/* create the socket */
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
return -1;
/* bind */
if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == 0) {
/* start listening */
if (listen(fd, LISTEN_BACKLOG) == 0)
return fd;
}
close_save_errno(fd);
return -1;
}
/* Accept a connection on a socket */
int net_accept(int fd, struct ip_addr *addr, unsigned int *port)
{
union sockaddr_union so;
int ret;
socklen_t addrlen;
i_assert(fd >= 0);
addrlen = sizeof(so);
ret = accept(fd, &so.sa, &addrlen);
if (ret < 0) {
if (errno == EBADF || errno == ENOTSOCK ||
errno == EOPNOTSUPP || errno == EFAULT || errno == EINVAL)
return -2;
else
return -1;
}
if (addr != NULL) sin_get_ip(&so, addr);
if (port != NULL) *port = sin_get_port(&so);
return ret;
}
/* Read data from socket, return number of bytes read, -1 = error */
ssize_t net_receive(int fd, void *buf, size_t len)
{
ssize_t ret;
i_assert(fd >= 0);
i_assert(buf != NULL);
i_assert(len <= SSIZE_T_MAX);
ret = recv(fd, buf, len, 0);
if (ret == 0) {
/* disconnected */
errno = 0;
return -2;
}
if (ret < 0) {
if (errno == EINTR || errno == EAGAIN)
return 0;
if (errno == ECONNRESET || errno == ETIMEDOUT) {
/* treat as disconnection */
return -2;
}
}
return ret;
}
/* Transmit data, return number of bytes sent, -1 = error */
ssize_t net_transmit(int fd, const void *data, size_t len)
{
ssize_t ret;
i_assert(fd >= 0);
i_assert(data != NULL);
i_assert(len <= SSIZE_T_MAX);
ret = send(fd, data, len, 0);
if (ret == -1 && (errno == EINTR || errno == EAGAIN))
return 0;
if (errno == EPIPE)
return -2;
return ret;
}
/* Get IP addresses for host. ips contains ips_count of IPs, they don't need
to be free'd. Returns 0 = ok, others = error code for net_gethosterror() */
int net_gethostbyname(const char *addr, struct ip_addr **ips, int *ips_count)
{
/* @UNSAFE */
#ifdef HAVE_IPV6
union sockaddr_union *so;
struct addrinfo hints, *ai, *origai;
char hbuf[NI_MAXHOST];
int host_error;
#else
struct hostent *hp;
#endif
int count;
i_assert(addr != NULL);
i_assert(ips != NULL);
i_assert(ips_count != NULL);
*ips = NULL;
*ips_count = 0;
#ifdef HAVE_IPV6
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
/* save error to host_error for later use */
host_error = getaddrinfo(addr, NULL, &hints, &ai);
if (host_error != 0)
return host_error;
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf,
sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
return 1;
/* get number of IPs */
origai = ai;
for (count = 0; ai != NULL; ai = ai->ai_next)
count++;
*ips_count = count;
*ips = t_malloc(sizeof(struct ip_addr) * count);
count = 0;
for (ai = origai; ai != NULL; ai = ai->ai_next, count++) {
so = (union sockaddr_union *) ai->ai_addr;
sin_get_ip(so, ips[count]);
}
freeaddrinfo(origai);
#else
hp = gethostbyname(addr);
if (hp == NULL)
return h_errno;
/* get number of IPs */
count = 0;
while (hp->h_addr_list[count] != NULL)
count++;
*ips_count = count;
*ips = t_malloc(sizeof(struct ip_addr) * count);
while (count > 0) {
count--;
(*ips)[count].family = AF_INET;
memcpy(&(*ips)[count].ip, hp->h_addr_list[count], 4);
}
#endif
return 0;
}
/* Get socket address/port */
int net_getsockname(int fd, struct ip_addr *addr, unsigned int *port)
{
union sockaddr_union so;
socklen_t addrlen;
i_assert(fd >= 0);
addrlen = sizeof(so);
if (getsockname(fd, (struct sockaddr *) &so, &addrlen) == -1)
return -1;
if (addr != NULL) sin_get_ip(&so, addr);
if (port != NULL) *port = sin_get_port(&so);
return 0;
}
const char *net_ip2addr(const struct ip_addr *ip)
{
#ifdef HAVE_IPV6
char addr[MAX_IP_LEN+1];
addr[MAX_IP_LEN] = '\0';
if (inet_ntop(ip->family, &ip->ip, addr, MAX_IP_LEN) == NULL)
return NULL;
return t_strdup(addr);
#else
unsigned long ip4;
if (ip->family != AF_INET)
return NULL;
ip4 = ntohl(ip->ip.s_addr);
return t_strdup_printf("%lu.%lu.%lu.%lu",
(ip4 & 0xff000000UL) >> 24,
(ip4 & 0x00ff0000) >> 16,
(ip4 & 0x0000ff00) >> 8,
(ip4 & 0x000000ff));
#endif
}
int net_addr2ip(const char *addr, struct ip_addr *ip)
{
if (strchr(addr, ':') != NULL) {
/* IPv6 */
ip->family = AF_INET6;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET6, addr, &ip->ip) == 0)
return -1;
#else
ip->ip.s_addr = 0;
#endif
} else {
/* IPv4 */
ip->family = AF_INET;
if (inet_aton(addr, (struct in_addr *) &ip->ip) == 0)
return -1;
}
return 0;
}
/* Get socket error */
int net_geterror(int fd)
{
int data;
socklen_t len = sizeof(data);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &data, &len) == -1)
return -1;
return data;
}
/* get error of net_gethostname() */
const char *net_gethosterror(int error)
{
#ifdef HAVE_IPV6
i_assert(error != 0);
if (error == 1) {
/* getnameinfo() failed */
return strerror(errno);
}
return gai_strerror(error);
#else
switch (error) {
case HOST_NOT_FOUND:
return "Host not found";
case NO_ADDRESS:
return "No IP address found for name";
case NO_RECOVERY:
return "A non-recovable name server error occurred";
case TRY_AGAIN:
return "A temporary error on an authoritative name server";
}
/* unknown error */
return NULL;
#endif
}
/* return TRUE if host lookup failed because it didn't exist (ie. not
some error with name server) */
int net_hosterror_notfound(int error)
{
#ifdef HAVE_IPV6
#ifdef EAI_NODATA /* NODATA is depricated */
return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
#else
return error != 1 && (error == EAI_NONAME);
#endif
#else
return error == HOST_NOT_FOUND || error == NO_ADDRESS;
#endif
}
/* Get name of TCP service */
char *net_getservbyport(unsigned short port)
{
struct servent *entry;
entry = getservbyport(htons(port), "tcp");
return entry == NULL ? NULL : entry->s_name;
}
int is_ipv4_address(const char *addr)
{
while (*addr != '\0') {
if (*addr != '.' && !i_isdigit(*addr))
return 0;
addr++;
}
return 1;
}
int is_ipv6_address(const char *addr)
{
while (*addr != '\0') {
if (*addr != ':' && !i_isxdigit(*addr))
return 0;
addr++;
}
return 1;
}