network.c revision 62505210a7e6d1b2e35fac335a6c875a7c98ccfb
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen network.c : Network stuff with IPv6 support
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen Copyright (c) 1999-2002 Timo Sirainen
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen Permission is hereby granted, free of charge, to any person obtaining
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen a copy of this software and associated documentation files (the
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen "Software"), to deal in the Software without restriction, including
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen without limitation the rights to use, copy, modify, merge, publish,
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen distribute, sublicense, and/or sell copies of the Software, and to
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen permit persons to whom the Software is furnished to do so, subject to
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen the following conditions:
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen The above copyright notice and this permission notice shall be
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen included in all copies or substantial portions of the Software.
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen/* copy IP to sockaddr */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainenstatic inline void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainenstatic inline void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic inline void sin_set_port(union sockaddr_union *so, unsigned int port)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen so->sin6.sin6_port = htons((unsigned short) port);
27902ecd70d903ee3b426ac363443268f8934b9aTimo Sirainen so->sin.sin_port = htons((unsigned short) port);
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainenstatic inline unsigned int sin_get_port(union sockaddr_union *so)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen/* Connect to socket with ip address */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenint net_connect_ip(IPADDR *ip, unsigned int port, IPADDR *my_ip)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (my_ip != NULL && ip->family != my_ip->family) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_warning("net_connect_ip(): ip->family != my_ip->family");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* create the socket */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* set socket options */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen /* set our own address */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* failed, set it back to INADDR_ANY */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* connect */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so));
8776322310b57a11c52cfb1822f35cf18b095525Timo Sirainen if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* too long path */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* create the socket */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* set socket options */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* connect */
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
0f3d4fbcf88e2ffd674893aed8cc1288fe17d290Timo Sirainen/* Disconnect socket */
27caac427c21cc358ca969fa1ccebfa476874123Timo Sirainen/* Set socket blocking/nonblocking */
27caac427c21cc358ca969fa1ccebfa476874123Timo Sirainenvoid net_set_nonblock(int fd __attr_unused__, int nonblock __attr_unused__)
f339a8e73beea7684ea634941ea82593dea522eeTimo Sirainen if (fcntl(fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0)
f339a8e73beea7684ea634941ea82593dea522eeTimo Sirainenvoid net_set_cork(int fd __attr_unused__, int cork __attr_unused__)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)) < 0)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* Listen for connections on a socket. if `my_ip' is NULL, listen in any
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainenint net_listen(IPADDR *my_ip, unsigned int *port)
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen /* create the socket */
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen fd = socket(so.sin.sin_family, SOCK_STREAM, 0);
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen if (fd == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen /* IPv6 is not supported by OS */
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen /* set socket options */
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen /* specify the address/port we want to listen in */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* get the actual port we started listen */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* start listening */
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen /* too long path */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* create the socket */
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen /* set socket options */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == 0) {
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* start listening */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* Accept a connection on a socket */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainenint net_accept(int fd, IPADDR *addr, unsigned int *port)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* Read data from socket, return number of bytes read, -1 = error */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainenssize_t net_receive(int fd, void *buf, size_t len)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (ret < 0 && (errno == EINTR || errno == EAGAIN))
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* Transmit data, return number of bytes sent, -1 = error */
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainenssize_t net_transmit(int fd, const void *data, size_t len)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (ret == -1 && (errno == EINTR || errno == EPIPE || errno == EAGAIN))
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* Get IP addresses for host. ips contains ips_count of IPs, they don't need
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen to be free'd. Returns 0 = ok, others = error code for net_gethosterror() */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainenint net_gethostbyname(const char *addr, IPADDR **ips, int *ips_count)
74375781364492f62348e1f873d9fa72fb1ec540Timo Sirainen /* save error to host_error for later use */
d646a63462a696824b1facd73e00732d4ac0f3dcTimo Sirainen host_error = getaddrinfo(addr, NULL, &hints, &ai);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf,
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* get number of IPs */
d646a63462a696824b1facd73e00732d4ac0f3dcTimo Sirainen for (ai = origai; ai != NULL; ai = ai->ai_next, count++) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* get number of IPs */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen while (count > 0) {
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen memcpy(&(*ips)[count].ip, hp->h_addr_list[count], 4);
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* Get socket address/port */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainenint net_getsockname(int fd, IPADDR *addr, unsigned int *port)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (getsockname(fd, (struct sockaddr *) &so, &addrlen) == -1)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (!inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN))
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen unsigned long ip4;
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen i_snprintf(host, MAX_IP_LEN, "%lu.%lu.%lu.%lu",
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (inet_aton(host, (struct in_addr *) &ip->ip) == 0)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* Get socket error */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &data, &len) == -1)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* get error of net_gethostname() */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* getnameinfo() failed */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen return "Host not found";
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen return "No IP address found for name";
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen return "A non-recovable name server error occurred";
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen return "A temporary error on an authoritative name server";
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen /* unknown error */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* return TRUE if host lookup failed because it didn't exist (ie. not
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen some error with name server) */
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen return error == HOST_NOT_FOUND || error == NO_ADDRESS;
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen/* Get name of TCP service */