network.c revision 0b56ba659328b7186b74c764702ecbe403c98839
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/*
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen network.c : Network stuff with IPv6 support
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen Copyright (c) 1999-2002 Timo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen Permission is hereby granted, free of charge, to any person obtaining
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen a copy of this software and associated documentation files (the
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen "Software"), to deal in the Software without restriction, including
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen without limitation the rights to use, copy, modify, merge, publish,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen distribute, sublicense, and/or sell copies of the Software, and to
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen permit persons to whom the Software is furnished to do so, subject to
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen the following conditions:
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen The above copyright notice and this permission notice shall be
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen included in all copies or substantial portions of the Software.
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
ae46f6ba5bb9eee8900254d3042e89d490023be0Timo Sirainen SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen*/
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
3f3ad16ff74d694796d22501250a9a29997c0729Timo Sirainen#include "lib.h"
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen#include "network.h"
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#include <unistd.h>
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#include <fcntl.h>
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#include <ctype.h>
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#include <sys/un.h>
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen#include <netinet/tcp.h>
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#define LISTEN_BACKLOG 8
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainenunion sockaddr_union {
8cafec427a378daf68c253929232d498509d548cTimo Sirainen struct sockaddr sa;
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen struct sockaddr_in sin;
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen#ifdef HAVE_IPV6
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen struct sockaddr_in6 sin6;
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen#endif
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi};
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi#ifdef HAVE_IPV6
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi sizeof(so.sin6) : sizeof(so.sin))
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi#else
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi# define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi#endif
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomiint net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2)
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen{
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen if (ip1->family != ip2->family)
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen return 0;
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen#ifdef HAVE_IPV6
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen if (ip1->family == AF_INET6)
b44033e45e9f48f8a6e1ac5905234fec5de6d6ccAki Tuomi return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
4dc8d682c855ca78db8874e04302e885465c1d65Timo Sirainen#endif
4dc8d682c855ca78db8874e04302e885465c1d65Timo Sirainen
d3bae1f9d2448e5c398145ea250849ec12583845Timo Sirainen return memcmp(&ip1->ip, &ip2->ip, 4) == 0;
d3bae1f9d2448e5c398145ea250849ec12583845Timo Sirainen}
5754fa860405e9af20c38981942f6aa97ce3158dTimo Sirainen
5754fa860405e9af20c38981942f6aa97ce3158dTimo Sirainen
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi/* copy IP to sockaddr */
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvistatic inline void
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvisin_set_ip(union sockaddr_union *so, const struct ip_addr *ip)
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi{
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi if (ip == NULL) {
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen#ifdef HAVE_IPV6
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen so->sin6.sin6_family = AF_INET6;
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen so->sin6.sin6_addr = in6addr_any;
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen#else
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen so->sin.sin_family = AF_INET;
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen so->sin.sin_addr.s_addr = INADDR_ANY;
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen#endif
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen return;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen }
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen so->sin.sin_family = ip->family;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#ifdef HAVE_IPV6
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (ip->family == AF_INET6)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen else
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#endif
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen memcpy(&so->sin.sin_addr, &ip->ip, 4);
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen}
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainenstatic inline void
6468191d64827a2d1481c091ec499874583c834eTimo Sirainensin_get_ip(const union sockaddr_union *so, struct ip_addr *ip)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen{
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen ip->family = so->sin.sin_family;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#ifdef HAVE_IPV6
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (ip->family == AF_INET6)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen else
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#endif
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen memcpy(&ip->ip, &so->sin.sin_addr, 4);
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen}
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainenstatic inline void sin_set_port(union sockaddr_union *so, unsigned int port)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen{
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#ifdef HAVE_IPV6
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (so->sin.sin_family == AF_INET6)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen so->sin6.sin6_port = htons((unsigned short) port);
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen else
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#endif
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen so->sin.sin_port = htons((unsigned short) port);
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen}
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
5545acdd3aa90a6e0cca2b665f909ec4c2fb2513Baofengstatic inline unsigned int sin_get_port(union sockaddr_union *so)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen{
009217abb57a24a4076092e8e4e165545747839eStephan Bosch#ifdef HAVE_IPV6
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (so->sin.sin_family == AF_INET6)
973c8fc1d7e9f982f7caf6385adb78dfacd9fb80Timo Sirainen return ntohs(so->sin6.sin6_port);
973c8fc1d7e9f982f7caf6385adb78dfacd9fb80Timo Sirainen#endif
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen return ntohs(so->sin.sin_port);
5545acdd3aa90a6e0cca2b665f909ec4c2fb2513Baofeng}
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainenstatic inline void close_save_errno(int fd)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen{
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen int old_errno = errno;
c1d01419ffbeb0e00f86a653db70bfd47110e7fcTimo Sirainen (void)close(fd);
009217abb57a24a4076092e8e4e165545747839eStephan Bosch errno = old_errno;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen}
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
009217abb57a24a4076092e8e4e165545747839eStephan Bosch/* Connect to socket with ip address */
6468191d64827a2d1481c091ec499874583c834eTimo Sirainenint net_connect_ip(const struct ip_addr *ip, unsigned int port,
009217abb57a24a4076092e8e4e165545747839eStephan Bosch const struct ip_addr *my_ip)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen{
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen union sockaddr_union so;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen int fd, ret, opt = 1;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (my_ip != NULL && ip->family != my_ip->family) {
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen i_warning("net_connect_ip(): ip->family != my_ip->family");
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen my_ip = NULL;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen }
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen /* create the socket */
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen memset(&so, 0, sizeof(so));
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen so.sin.sin_family = ip->family;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen fd = socket(ip->family, SOCK_STREAM, 0);
5545acdd3aa90a6e0cca2b665f909ec4c2fb2513Baofeng
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (fd == -1)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen return -1;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen /* set socket options */
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen /* set our own address */
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (my_ip != NULL) {
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen sin_set_ip(&so, my_ip);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* failed, set it back to INADDR_ANY */
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen sin_set_ip(&so, NULL);
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen bind(fd, &so.sa, SIZEOF_SOCKADDR(so));
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen }
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen }
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* connect */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen sin_set_ip(&so, ip);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen sin_set_port(&so, port);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so));
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#ifndef WIN32
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (ret < 0 && errno != EINPROGRESS)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#else
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#endif
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen {
faec0abfd648c647030027e86de2ce8911df683bTimo Sirainen close_save_errno(fd);
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen return -1;
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen }
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen return fd;
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen}
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainenint net_connect_unix(const char *path)
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen{
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen struct sockaddr_un sa;
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen int fd, ret;
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen memset(&sa, 0, sizeof(sa));
300e4e43ed1ca46d0614459161ca2fb460ef661aTimo Sirainen sa.sun_family = AF_UNIX;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (strocpy(sa.sun_path, path, sizeof(sa.sun_path)) < 0) {
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen /* too long path */
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen errno = EINVAL;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen return -1;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen }
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen /* create the socket */
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen fd = socket(PF_UNIX, SOCK_STREAM, 0);
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (fd == -1)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen return -1;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen /* connect */
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (ret < 0 && errno != EINPROGRESS) {
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen close_save_errno(fd);
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen return -1;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen }
861f53be0cc2fa5665f3c107a7576e2a53bb2eb0Timo Sirainen
b4ddb5b3c3722620a8fef387dd8c47bb411a5643Timo Sirainen return fd;
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen}
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen/* Disconnect socket */
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainenvoid net_disconnect(int fd)
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen{
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen if (close(fd) < 0)
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen i_error("net_disconnect() failed: %m");
db3b95d5a33ddce552d41136ae68d7331f8bf5feTimo Sirainen}
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen/* Set socket blocking/nonblocking */
6468191d64827a2d1481c091ec499874583c834eTimo Sirainenvoid net_set_nonblock(int fd __attr_unused__, int nonblock __attr_unused__)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen{
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#ifdef HAVE_FCNTL
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen if (fcntl(fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0)
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen i_fatal("net_set_nonblock() failed: %m");
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen#endif
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen}
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenint net_set_cork(int fd __attr_unused__, int cork __attr_unused__)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#ifdef TCP_CORK
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#else
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen errno = ENOPROTOOPT;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return -1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#endif
5a444fdfabcc0c75708c21e84dc8b87eddab7335Timo Sirainen}
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid net_get_ip_any4(struct ip_addr *ip)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainen struct in_addr *in_ip = (struct in_addr *) &ip->ip;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ip->family = AF_INET;
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainen in_ip->s_addr = INADDR_ANY;
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen}
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainenvoid net_get_ip_any6(struct ip_addr *ip)
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainen{
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#ifdef HAVE_IPV6
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ip->family = AF_INET6;
2efe19d9045768d985a3bd549cff12f65ba40cc8Timo Sirainen ip->ip = in6addr_any;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#else
5b4d189a01d248458496068f838128f1bafdcf2eTimo Sirainen memset(ip, 0, sizeof(struct ip_addr));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#endif
6ecc5475f7efd4dcdf4ce727191693de24c5cf51Timo Sirainen}
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen/* Listen for connections on a socket. if `my_ip' is NULL, listen in any
7ee226c2a66aa4dce7f13e8b17687db285c981bdTimo Sirainen address. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenint net_listen(const struct ip_addr *my_ip, unsigned int *port)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen union sockaddr_union so;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch int ret, fd, opt = 1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen socklen_t len;
958e5ae51a755558b6d022a39b194614726b4225Timo Sirainen
958e5ae51a755558b6d022a39b194614726b4225Timo Sirainen i_assert(port != NULL);
958e5ae51a755558b6d022a39b194614726b4225Timo Sirainen
958e5ae51a755558b6d022a39b194614726b4225Timo Sirainen memset(&so, 0, sizeof(so));
958e5ae51a755558b6d022a39b194614726b4225Timo Sirainen sin_set_port(&so, *port);
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen sin_set_ip(&so, my_ip);
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen /* create the socket */
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen fd = socket(so.sin.sin_family, SOCK_STREAM, 0);
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen#ifdef HAVE_IPV6
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (fd == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* IPv6 is not supported by OS */
c1d01419ffbeb0e00f86a653db70bfd47110e7fcTimo Sirainen so.sin.sin_family = AF_INET;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen so.sin.sin_addr.s_addr = INADDR_ANY;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen fd = socket(AF_INET, SOCK_STREAM, 0);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen }
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#endif
c3a2a487e23a282e59254b82deb9344ed0306bb2Timo Sirainen if (fd == -1)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return -1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* set socket options */
c4900d31385344bfadaee53a897daeafdb3063d8Timo Sirainen setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen /* specify the address/port we want to listen in */
edd318d5866ac3fbc6e8df28fb24a4dfef93c884Timo Sirainen ret = bind(fd, &so.sa, SIZEOF_SOCKADDR(so));
69b22a0c0c84087e5bdeec71faae7ea77295240fTimo Sirainen if (ret >= 0) {
69b22a0c0c84087e5bdeec71faae7ea77295240fTimo Sirainen /* get the actual port we started listen */
69b22a0c0c84087e5bdeec71faae7ea77295240fTimo Sirainen len = SIZEOF_SOCKADDR(so);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret = getsockname(fd, &so.sa, &len);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (ret >= 0) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *port = sin_get_port(&so);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek /* start listening */
8dd76854cc680053986142d5f5e823f637447929Timo Sirainen if (listen(fd, LISTEN_BACKLOG) >= 0)
8dd76854cc680053986142d5f5e823f637447929Timo Sirainen return fd;
faa01447c2f699b63ceccf129430a9ed46458083Timo Sirainen }
faa01447c2f699b63ceccf129430a9ed46458083Timo Sirainen
faa01447c2f699b63ceccf129430a9ed46458083Timo Sirainen }
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
c3a2a487e23a282e59254b82deb9344ed0306bb2Timo Sirainen /* error */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen close_save_errno(fd);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return -1;
6468191d64827a2d1481c091ec499874583c834eTimo Sirainen}
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenint net_listen_unix(const char *path)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen struct sockaddr_un sa;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen int fd;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen memset(&sa, 0, sizeof(sa));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen sa.sun_family = AF_UNIX;
412b772c337428b72149605c1410524c2353e5d4Timo Sirainen if (strocpy(sa.sun_path, path, sizeof(sa.sun_path)) < 0) {
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen /* too long path */
009217abb57a24a4076092e8e4e165545747839eStephan Bosch errno = EINVAL;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* create the socket */
412b772c337428b72149605c1410524c2353e5d4Timo Sirainen fd = socket(PF_UNIX, SOCK_STREAM, 0);
f9f77e06a148fd0816004e0e1b0f585307148a7dTimo Sirainen if (fd == -1)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* bind */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == 0) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* start listening */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (listen(fd, LISTEN_BACKLOG) == 0)
009217abb57a24a4076092e8e4e165545747839eStephan Bosch return fd;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen close_save_errno(fd);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen/* Accept a connection on a socket */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenint net_accept(int fd, struct ip_addr *addr, unsigned int *port)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen{
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen union sockaddr_union so;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen int ret;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen socklen_t addrlen;
7ee226c2a66aa4dce7f13e8b17687db285c981bdTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_assert(fd >= 0);
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen
e2fdcdb4ee53ab769123e27997713aaea34910e1Timo Sirainen addrlen = sizeof(so);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = accept(fd, &so.sa, &addrlen);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (ret < 0) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (errno == EBADF || errno == ENOTSOCK ||
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen errno == EOPNOTSUPP || errno == EFAULT || errno == EINVAL)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -2;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen else
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
d6b3cfd855c0eebed68be50d3111de1b5a6afeb0Timo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (addr != NULL) sin_get_ip(&so, addr);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (port != NULL) *port = sin_get_port(&so);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return ret;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen}
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen
/* 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_ip2host(const struct ip_addr *ip)
{
#ifdef HAVE_IPV6
char host[MAX_IP_LEN+1];
host[MAX_IP_LEN] = '\0';
if (inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN) == NULL)
return NULL;
return t_strdup(host);
#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
return 0;
}
int net_host2ip(const char *host, struct ip_addr *ip)
{
if (strchr(host, ':') != NULL) {
/* IPv6 */
ip->family = AF_INET6;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET6, host, &ip->ip) == 0)
return -1;
#else
ip->ip.s_addr = 0;
#endif
} else {
/* IPv4 */
ip->family = AF_INET;
if (inet_aton(host, (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
return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
#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 *host)
{
while (*host != '\0') {
if (*host != '.' && !i_isdigit(*host))
return 0;
host++;
}
return 1;
}
int is_ipv6_address(const char *host)
{
while (*host != '\0') {
if (*host != ':' && !i_isxdigit(*host))
return 0;
host++;
}
return 1;
}