net.c revision efbeceffc8cb1602d698151b0246f29e13b980fa
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 1999-2014 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define _GNU_SOURCE /* For Linux's struct ucred */
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen# include <sys/ucred.h> /* for FreeBSD struct xucred */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
8fa41238067c854435884c459963fde6f8c6436bTimo Sirainen#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && !defined(HAVE_GETPEERUCRED) && defined(MSG_WAITALL) && defined(LOCAL_CREDS)
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainenbool net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2)
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainenint net_ip_cmp(const struct ip_addr *ip1, const struct ip_addr *ip2)
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainen return memcmp(&ip1->u.ip6, &ip2->u.ip6, sizeof(ip1->u.ip6));
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainen return memcmp(&ip1->u.ip4, &ip2->u.ip4, sizeof(ip1->u.ip4));
5aeb15e5817fbd4b1d8de540aa7673e3819a8030Timo Sirainenunsigned int net_ip_hash(const struct ip_addr *ip)
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen const unsigned char *p;
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen unsigned int len, g, h = 0;
9f32b9444d2a6db8f556d2c49ffceab1a59791ffTimo Sirainen h = (h << 4) + *p;
9f32b9444d2a6db8f556d2c49ffceab1a59791ffTimo Sirainen if ((g = h & 0xf0000000UL)) {
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen h = h ^ (g >> 24);
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen/* copy IP to sockaddr */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenstatic inline void
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainensin_set_ip(union sockaddr_union *so, const struct ip_addr *ip)
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen memcpy(&so->sin6.sin6_addr, &ip->u.ip6, sizeof(ip->u.ip6));
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen memcpy(&so->sin.sin_addr, &ip->u.ip4, sizeof(ip->u.ip4));
e704b4ab2c204e538f87b2a90aca68c0deac6296Timo Sirainenstatic inline void
96595bf98ed9264b2b35700a640daf53debc3082Timo Sirainensin_get_ip(const union sockaddr_union *so, struct ip_addr *ip)
96595bf98ed9264b2b35700a640daf53debc3082Timo Sirainen /* IP structs may be sent across processes. Clear the whole struct
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen first to make sure it won't leak any data across processes. */
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen memcpy(&ip->u.ip6, &so->sin6.sin6_addr, sizeof(ip->u.ip6));
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen memcpy(&ip->u.ip4, &so->sin.sin_addr, sizeof(ip->u.ip4));
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainenstatic inline void sin_set_port(union sockaddr_union *so, unsigned int port)
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen so->sin6.sin6_port = htons((unsigned short) port);
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen so->sin.sin_port = htons((unsigned short) port);
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainenstatic inline unsigned int sin_get_port(union sockaddr_union *so)
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainennet_connect_ip_full_freebsd(const struct ip_addr *ip, unsigned int port,
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainenstatic int net_connect_ip_full(const struct ip_addr *ip, unsigned int port,
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainen for (try = 0;;) {
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainen fd = net_connect_ip_full_freebsd(ip, port, my_ip, blocking);
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen This may be just a temporary problem:
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen EADDRINUSE: busy
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen EACCES: pf may cause this if another connection used
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen the same port recently
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen/* then some kludging: */
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen#define net_connect_ip_full net_connect_ip_full_freebsd
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainenstatic int net_connect_ip_full(const struct ip_addr *ip, unsigned int port,
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen const struct ip_addr *my_ip, int sock_type, bool blocking)
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen if (my_ip != NULL && ip->family != my_ip->family) {
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen i_warning("net_connect_ip(): ip->family != my_ip->family");
d161e3c2cde2bd8d5917840f68823a2259ed426eTimo Sirainen /* create the socket */
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen /* set socket options */
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen (void)setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* set our own address */
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen i_error("bind(%s) failed: %m", net_ip2addr(my_ip));
ea91861234475257f436dc07925f80cf4ac32b71Timo Sirainen /* connect */
ea91861234475257f436dc07925f80cf4ac32b71Timo Sirainen ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so));
ea91861234475257f436dc07925f80cf4ac32b71Timo Sirainen if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainenint net_connect_ip(const struct ip_addr *ip, unsigned int port,
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen return net_connect_ip_full(ip, port, my_ip, SOCK_STREAM, FALSE);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenint net_connect_ip_blocking(const struct ip_addr *ip, unsigned int port,
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen return net_connect_ip_full(ip, port, my_ip, SOCK_STREAM, TRUE);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainenint net_connect_udp(const struct ip_addr *ip, unsigned int port,
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen return net_connect_ip_full(ip, port, my_ip, SOCK_DGRAM, FALSE);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* create the socket */
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) {
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* too long path */
f23298fea47eecbeded985ee2537a34c4c4ef56bTimo Sirainen /* create the socket */
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* connect */
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof on)) {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_error("setsockopt(LOCAL_CREDS) failed: %m");
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainenint net_connect_unix_with_retries(const char *path, unsigned int msecs)
6825360d446542046757b06064282301c4c6b27cTimo Sirainen if (fd != -1 || (errno != EAGAIN && errno != ECONNREFUSED))
6825360d446542046757b06064282301c4c6b27cTimo Sirainen /* busy. wait for a while. */
c8d093d149253fe8faec267c5057f45fe626f84cTimo Sirainen } while (timeval_diff_msecs(&now, &start) < (int)msecs);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen /* FreeBSD's close() fails with ECONNRESET if socket still has unsent
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen data in transmit buffer. We don't care. */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenint net_set_cork(int fd ATTR_UNUSED, bool cork ATTR_UNUSED)
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen return setsockopt(fd, IPPROTO_TCP, TCP_CORK, &val, sizeof(val));
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainenint net_listen(const struct ip_addr *my_ip, unsigned int *port, int backlog)
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen return net_listen_full(my_ip, port, &flags, backlog);
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainenint net_listen_full(const struct ip_addr *my_ip, unsigned int *port,
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* create the socket */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen fd = socket(so.sin.sin_family, SOCK_STREAM, 0);
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* IPv6 is not supported by OS */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* set socket options */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen (void)setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen if ((*flags & NET_LISTEN_FLAG_REUSEPORT) != 0) {
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* If using IPv6, bind only to IPv6 if possible. This avoids
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen ambiguities with IPv4-mapped IPv6 addresses. */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen (void)setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* specify the address/port we want to listen in */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen my_ip == NULL ? "" : net_ip2addr(my_ip), *port);
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* get the actual port we started listen */
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen /* start listening */
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainenint net_listen_unix(const char *path, int backlog)
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) {
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen /* too long path */
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen /* create the socket */
e3689d0f073341e844638f34e1e4d0b7bb053cc8Timo Sirainen if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof on)) {
e3689d0f073341e844638f34e1e4d0b7bb053cc8Timo Sirainen i_error("setsockopt(LOCAL_CREDS) failed: %m");
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen /* start listening */
14b8bbb81e0b546436d4d5d5f38e45027c146b9bTimo Sirainenint net_listen_unix_unlink_stale(const char *path, int backlog)
14b8bbb81e0b546436d4d5d5f38e45027c146b9bTimo Sirainen unsigned int i = 0;
14b8bbb81e0b546436d4d5d5f38e45027c146b9bTimo Sirainen while ((fd = net_listen_unix(path, backlog)) == -1) {
e8fd7988ec183fb6c104aed19a61f1a096c51d34Timo Sirainen /* see if it really exists */
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen /* delete and try again */
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainenint net_accept(int fd, struct ip_addr *addr_r, unsigned int *port_r)
94aa90d2d17a7aebcda5a4193a62e80ddbb169b7Timo Sirainen if (port_r != NULL) *port_r = sin_get_port(&so);
af1e2b2ab5d1c5ca5afe482ef8c8161c17acc190Timo Sirainenssize_t net_receive(int fd, void *buf, size_t len)
da5d50534cfca45d0aaaf0bdac17b287b4588809Timo Sirainen /* disconnected */
f01d1332d49dbd34baef4601ac7b3cc557021084Timo Sirainen if (errno == ECONNRESET || errno == ETIMEDOUT) {
f01d1332d49dbd34baef4601ac7b3cc557021084Timo Sirainen /* treat as disconnection */
f01d1332d49dbd34baef4601ac7b3cc557021084Timo Sirainenssize_t net_transmit(int fd, const void *data, size_t len)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint net_gethostbyname(const char *addr, struct ip_addr **ips,
9f32b9444d2a6db8f556d2c49ffceab1a59791ffTimo Sirainen unsigned int *ips_count)
f01d1332d49dbd34baef4601ac7b3cc557021084Timo Sirainen /* @UNSAFE */
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen /* save error to host_error for later use */
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen host_error = getaddrinfo(addr, NULL, &hints, &ai);
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen /* get number of IPs */
0bd15afe9cadd09b01e68b493b30c9d7e92b4095Timo Sirainen *ips = t_malloc(sizeof(struct ip_addr) * count);
9e0b187933b52db68c1aefb913970eeba47b986eAki Tuomi for (ai = origai; ai != NULL; ai = ai->ai_next, count++) {
ac26a4607cb12b156f6a42f1ead2881bedd43d94Timo Sirainen /* get number of IPs */
41e6eb07b411ea58352ba9d2cc8cf340325d49f3Timo Sirainen *ips = t_malloc(sizeof(struct ip_addr) * count);
41e6eb07b411ea58352ba9d2cc8cf340325d49f3Timo Sirainen while (count > 0) {
41e6eb07b411ea58352ba9d2cc8cf340325d49f3Timo Sirainen memcpy(&(*ips)[count].u.ip4, hp->h_addr_list[count],
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint net_gethostbyaddr(const struct ip_addr *ip, const char **name_r)
5ea06115cb60413b62ffb58ffdd62786fec6a316Timo Sirainen ret = getnameinfo(&so.sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
90c5979b3c530707744beab6413f9d1e446335d1Timo Sirainenint net_getsockname(int fd, struct ip_addr *addr, unsigned int *port)
4d4d585520538a752e9f0a4a1c019a2918f52e56Timo Sirainenint net_getpeername(int fd, struct ip_addr *addr, unsigned int *port)
4d4d585520538a752e9f0a4a1c019a2918f52e56Timo Sirainenint net_getunixname(int fd, const char **name_r)
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainenint net_getunixcred(int fd, struct net_unix_cred *cred_r)
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen /* OpenBSD (may also provide getpeereid, but we also want pid) */
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
6143fece58262865ce89b5012b73ef08f2ad6abcTimo Sirainen i_error("getsockopt(SO_PEERCRED) failed: %m");
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen /* NetBSD (may also provide getpeereid, but we also want pid) */
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (getsockopt(fd, 0, LOCAL_PEEREID, &ucred, &len) < 0) {
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen i_error("getsockopt(LOCAL_PEEREID) failed: %m");
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen /* OSX 10.4+, FreeBSD 4.6+, OpenBSD 3.0+, NetBSD 5.0+ */
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen if (getpeereid(fd, &cred_r->uid, &cred_r->gid) < 0) {
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen /* Older FreeBSD */
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen if (getsockopt(fd, 0, LOCAL_PEERCRED, &ucred, &len) < 0) {
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen i_error("getsockopt(LOCAL_PEERCRED) failed: %m");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* Solaris */
15cc66ca72982a43e3bfa58f307adc57e9caa52dTimo Sirainen /* NetBSD < 5 */
a045c3aba2610c6ed0bf1c346df1c6d8f7b9fbfdTimo Sirainen sc->sc_uid = sc->sc_euid = sc->sc_gid = sc->sc_egid = -1;
5cc772dc8b507be0bc1996b5717943ba13432e08Timo Sirainen msg.msg_controllen = sizeof(cdata.ch) + sizeof(cdata.buf);
5cc772dc8b507be0bc1996b5717943ba13432e08Timo Sirainen for (i = 0; i < 10; i++) {
5cc772dc8b507be0bc1996b5717943ba13432e08Timo Sirainen n = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK);
31189eeac1ccaaf1201c60427f8c1087b0f5dfceTimo Sirainenconst char *net_ip2addr(const struct ip_addr *ip)
0d0451206a91e9f96e522075dce28a89adc2325dTimo Sirainen if (inet_ntop(ip->family, &ip->u.ip6, addr, MAX_IP_LEN) == NULL)
87712707722ef7d73acb065546e61afa4455cd9eTimo Sirainen unsigned long ip4;
651fc0f1e43fef3e02e0e7b5f498973b05f641d7Timo Sirainenint net_addr2ip(const char *addr, struct ip_addr *ip)
bb444f746dc6c15a8d0af67ef81bfa48c28471d0Timo Sirainen /* allow [ipv6 addr] */
1f80b32fc28f7a723ff07c1694230a090808b506Timo Sirainenint net_ipv6_mapped_ipv4_convert(const struct ip_addr *src,
1f80b32fc28f7a723ff07c1694230a090808b506Timo Sirainen if (memcmp(src->u.ip6.s6_addr, v4_prefix, sizeof(v4_prefix)) != 0)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen memcpy(&dest->u.ip6, &src->u.ip6.s6_addr[3*4], 4);
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &data, &len) == -1) {
1f80b32fc28f7a723ff07c1694230a090808b506Timo Sirainen /* we're now really returning the getsockopt()'s error code
1f80b32fc28f7a723ff07c1694230a090808b506Timo Sirainen instead of the socket's, but normally we should never get
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen here anyway. */
eba201921d0e5ddc2319e94359576fbcba78eb41Aki Tuomi return "Host not found";
eba201921d0e5ddc2319e94359576fbcba78eb41Aki Tuomi return "No IP address found for name";
eba201921d0e5ddc2319e94359576fbcba78eb41Aki Tuomi return "A non-recoverable name server error occurred";
eba201921d0e5ddc2319e94359576fbcba78eb41Aki Tuomi return "A temporary error on an authoritative name server";
eba201921d0e5ddc2319e94359576fbcba78eb41Aki Tuomi return t_strdup_printf("Unknown error %d", error);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
e8fd7988ec183fb6c104aed19a61f1a096c51d34Timo Sirainen return error == HOST_NOT_FOUND || error == NO_ADDRESS;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainenconst char *net_getservbyport(unsigned short port)
addr++;
return FALSE;
addr++;
return TRUE;
unsigned int *bits_r)
if (p != NULL)
if (p == NULL) {
unsigned int pos, i;
return FALSE;
return FALSE;
#ifdef HAVE_IPV6
return FALSE;
return FALSE;
return FALSE;
return FALSE;
return TRUE;