1/* 2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com> 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13*/ 14 15/*! 16 * @file 17 * Netatalk utility functions 18 */ 19 20#ifdef HAVE_CONFIG_H 21#include "config.h" 22#endif /* HAVE_CONFIG_H */ 23 24#if !defined(__FreeBSD__) 25# ifndef _XOPEN_SOURCE 26# define _XOPEN_SOURCE 600 27# endif 28# ifndef __EXTENSIONS__ 29# define __EXTENSIONS__ 30# endif 31# ifndef _GNU_SOURCE 32# define _GNU_SOURCE 33# endif 34#endif 35#include <unistd.h> 36#include <fcntl.h> 37#include <sys/types.h> 38#include <sys/socket.h> 39#include <arpa/inet.h> 40#include <netinet/in.h> 41#include <stdlib.h> 42#include <string.h> 43#include <errno.h> 44#include <sys/time.h> 45#include <time.h> 46#include <sys/ioctl.h> 47 48#include <atalk/logger.h> 49#include <atalk/util.h> 50 51static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff}; 52 53/*! 54 * @brief set or unset non-blocking IO on a fd 55 * 56 * @param fd (r) File descriptor 57 * @param cmd (r) 0: disable non-blocking IO, ie block\n 58 * <>0: enable non-blocking IO 59 * 60 * @returns 0 on success, -1 on failure 61 */ 62int setnonblock(int fd, int cmd) 63{ 64 int ofdflags; 65 int fdflags; 66 67 if ((fdflags = ofdflags = fcntl(fd, F_GETFL, 0)) == -1) 68 return -1; 69 70 if (cmd) 71 fdflags |= O_NONBLOCK; 72 else 73 fdflags &= ~O_NONBLOCK; 74 75 if (fdflags != ofdflags) 76 if (fcntl(fd, F_SETFL, fdflags) == -1) 77 return -1; 78 79 return 0; 80} 81 82/*! 83 * non-blocking drop-in replacement for read with timeout using select 84 * 85 * @param socket (r) socket, if in blocking mode, pass "setnonblocking" arg as 1 86 * @param data (rw) buffer for the read data 87 * @param lenght (r) how many bytes to read 88 * @param setnonblocking (r) when non-zero this func will enable and disable non blocking 89 * io mode for the socket 90 * @param timeout (r) number of seconds to try reading 91 * 92 * @returns number of bytes actually read or -1 on timeout or error 93 */ 94ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, int timeout) 95{ 96 size_t stored = 0; 97 ssize_t len = 0; 98 struct timeval now, end, tv; 99 fd_set rfds; 100 int ret; 101 102 FD_ZERO(&rfds); 103 104 if (setnonblocking) { 105 if (setnonblock(socket, 1) != 0) 106 return -1; 107 } 108 109 /* Calculate end time */ 110 (void)gettimeofday(&now, NULL); 111 end = now; 112 end.tv_sec += timeout; 113 114 while (stored < length) { 115 len = read(socket, (char *) data + stored, length - stored); 116 if (len == -1) { 117 switch (errno) { 118 case EINTR: 119 continue; 120 case EAGAIN: 121 FD_SET(socket, &rfds); 122 tv.tv_usec = 0; 123 tv.tv_sec = timeout; 124 125 while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) { 126 switch (ret) { 127 case 0: 128 LOG(log_debug, logtype_afpd, "select timeout %d s", timeout); 129 errno = EAGAIN; 130 goto exit; 131 132 default: /* -1 */ 133 switch (errno) { 134 case EINTR: 135 (void)gettimeofday(&now, NULL); 136 if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) { 137 LOG(log_debug, logtype_afpd, "select timeout %d s", timeout); 138 goto exit; 139 } 140 if (now.tv_usec > end.tv_usec) { 141 tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec; 142 tv.tv_sec = end.tv_sec - now.tv_sec - 1; 143 } else { 144 tv.tv_usec = end.tv_usec - now.tv_usec; 145 tv.tv_sec = end.tv_sec - now.tv_sec; 146 } 147 FD_SET(socket, &rfds); 148 continue; 149 case EBADF: 150 /* possibly entered disconnected state, don't spam log here */ 151 LOG(log_debug, logtype_afpd, "select: %s", strerror(errno)); 152 stored = -1; 153 goto exit; 154 default: 155 LOG(log_error, logtype_afpd, "select: %s", strerror(errno)); 156 stored = -1; 157 goto exit; 158 } 159 } 160 } /* while (select) */ 161 continue; 162 } /* switch (errno) */ 163 LOG(log_error, logtype_afpd, "read: %s", strerror(errno)); 164 stored = -1; 165 goto exit; 166 } /* (len == -1) */ 167 else if (len > 0) 168 stored += len; 169 else 170 break; 171 } /* while (stored < length) */ 172 173exit: 174 if (setnonblocking) { 175 if (setnonblock(socket, 0) != 0) 176 return -1; 177 } 178 179 if (len == -1 && stored == 0) 180 /* last read or select got an error and we haven't got yet anything => return -1*/ 181 return -1; 182 return stored; 183} 184 185/*! 186 * non-blocking drop-in replacement for read with timeout using select 187 * 188 * @param socket (r) socket, if in blocking mode, pass "setnonblocking" arg as 1 189 * @param data (rw) buffer for the read data 190 * @param lenght (r) how many bytes to read 191 * @param setnonblocking (r) when non-zero this func will enable and disable non blocking 192 * io mode for the socket 193 * @param timeout (r) number of seconds to try reading 194 * 195 * @returns number of bytes actually read or -1 on fatal error 196 */ 197ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout) 198{ 199 size_t stored = 0; 200 ssize_t len = 0; 201 struct timeval now, end, tv; 202 fd_set rfds; 203 int ret; 204 205 if (setnonblocking) { 206 if (setnonblock(socket, 1) != 0) 207 return -1; 208 } 209 210 /* Calculate end time */ 211 (void)gettimeofday(&now, NULL); 212 end = now; 213 end.tv_sec += timeout; 214 215 while (stored < length) { 216 len = write(socket, (char *) data + stored, length - stored); 217 if (len == -1) { 218 switch (errno) { 219 case EINTR: 220 continue; 221 case EAGAIN: 222 FD_ZERO(&rfds); 223 FD_SET(socket, &rfds); 224 tv.tv_usec = 0; 225 tv.tv_sec = timeout; 226 227 while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) { 228 switch (ret) { 229 case 0: 230 LOG(log_warning, logtype_afpd, "select timeout %d s", timeout); 231 goto exit; 232 233 default: /* -1 */ 234 if (errno == EINTR) { 235 (void)gettimeofday(&now, NULL); 236 if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) { 237 LOG(log_warning, logtype_afpd, "select timeout %d s", timeout); 238 goto exit; 239 } 240 if (now.tv_usec > end.tv_usec) { 241 tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec; 242 tv.tv_sec = end.tv_sec - now.tv_sec - 1; 243 } else { 244 tv.tv_usec = end.tv_usec - now.tv_usec; 245 tv.tv_sec = end.tv_sec - now.tv_sec; 246 } 247 FD_ZERO(&rfds); 248 FD_SET(socket, &rfds); 249 continue; 250 } 251 LOG(log_error, logtype_afpd, "select: %s", strerror(errno)); 252 stored = -1; 253 goto exit; 254 } 255 } /* while (select) */ 256 continue; 257 } /* switch (errno) */ 258 LOG(log_error, logtype_afpd, "read: %s", strerror(errno)); 259 stored = -1; 260 goto exit; 261 } /* (len == -1) */ 262 else if (len > 0) 263 stored += len; 264 else 265 break; 266 } /* while (stored < length) */ 267 268exit: 269 if (setnonblocking) { 270 if (setnonblock(socket, 0) != 0) 271 return -1; 272 } 273 274 if (len == -1 && stored == 0) 275 /* last read or select got an error and we haven't got yet anything => return -1*/ 276 return -1; 277 return stored; 278} 279 280/*! 281 * @brief convert an IPv4 or IPv6 address to a static string using inet_ntop 282 * 283 * IPv6 mapped IPv4 addresses are returned as IPv4 addreses eg 284 * ::ffff:10.0.0.0 is returned as "10.0.0.0". 285 * 286 * @param sa (r) pointer to an struct sockaddr 287 * 288 * @returns pointer to a static string cotaining the converted address as string.\n 289 * On error pointers to "0.0.0.0" or "::0" are returned. 290 */ 291const char *getip_string(const struct sockaddr *sa) 292{ 293 static char ip4[INET_ADDRSTRLEN]; 294 static char ip6[INET6_ADDRSTRLEN]; 295 296 switch (sa->sa_family) { 297 298 case AF_INET: { 299 const struct sockaddr_in *sai4 = (const struct sockaddr_in *)sa; 300 if ((inet_ntop(AF_INET, &(sai4->sin_addr), ip4, INET_ADDRSTRLEN)) == NULL) 301 return "0.0.0.0"; 302 return ip4; 303 } 304 case AF_INET6: { 305 const struct sockaddr_in6 *sai6 = (const struct sockaddr_in6 *)sa; 306 if ((inet_ntop(AF_INET6, &(sai6->sin6_addr), ip6, INET6_ADDRSTRLEN)) == NULL) 307 return "::0"; 308 309 /* Deal with IPv6 mapped IPv4 addresses*/ 310 if ((memcmp(sai6->sin6_addr.s6_addr, ipv4mapprefix, sizeof(ipv4mapprefix))) == 0) 311 return (strrchr(ip6, ':') + 1); 312 return ip6; 313 } 314 default: 315 return "getip_string ERROR"; 316 } 317 318 /* We never get here */ 319} 320 321/*! 322 * @brief return port number from struct sockaddr 323 * 324 * @param sa (r) pointer to an struct sockaddr 325 * 326 * @returns port as unsigned int 327 */ 328unsigned int getip_port(const struct sockaddr *sa) 329{ 330 if (sa->sa_family == AF_INET) { /* IPv4 */ 331 const struct sockaddr_in *sai4 = (const struct sockaddr_in *)sa; 332 return ntohs(sai4->sin_port); 333 } else { /* IPv6 */ 334 const struct sockaddr_in6 *sai6 = (const struct sockaddr_in6 *)sa; 335 return ntohs(sai6->sin6_port); 336 } 337 338 /* We never get here */ 339} 340 341/*! 342 * @brief apply netmask to IP (v4 or v6) 343 * 344 * Modifies IP address in sa->sin[6]_addr-s[6]_addr. The caller is responsible 345 * for passing a value for mask that is sensible to the passed address, 346 * eg 0 <= mask <= 32 for IPv4 or 0<= mask <= 128 for IPv6. mask > 32 for 347 * IPv4 is treated as mask = 32, mask > 128 is set to 128 for IPv6. 348 * 349 * @param ai (rw) pointer to an struct sockaddr 350 * @parma mask (r) number of maskbits 351 */ 352void apply_ip_mask(struct sockaddr *sa, int mask) 353{ 354 355 switch (sa->sa_family) { 356 case AF_INET: { 357 if (mask >= 32) 358 return; 359 360 struct sockaddr_in *si = (struct sockaddr_in *)sa; 361 uint32_t nmask = mask ? ~((1 << (32 - mask)) - 1) : 0; 362 si->sin_addr.s_addr &= htonl(nmask); 363 break; 364 } 365 case AF_INET6: { 366 if (mask >= 128) 367 return; 368 369 int i, maskbytes, maskbits; 370 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)sa; 371 372 /* Deal with IPv6 mapped IPv4 addresses*/ 373 if ((memcmp(si6->sin6_addr.s6_addr, ipv4mapprefix, sizeof(ipv4mapprefix))) == 0) { 374 mask += 96; 375 if (mask >= 128) 376 return; 377 } 378 379 maskbytes = (128 - mask) / 8; /* maskbytes really are those that will be 0'ed */ 380 maskbits = mask % 8; 381 382 for (i = maskbytes - 1; i >= 0; i--) 383 si6->sin6_addr.s6_addr[15 - i] = 0; 384 if (maskbits) 385 si6->sin6_addr.s6_addr[15 - maskbytes] &= ~((1 << (8 - maskbits)) - 1); 386 break; 387 } 388 default: 389 break; 390 } 391} 392 393/*! 394 * @brief compare IP addresses for equality 395 * 396 * @param sa1 (r) pointer to an struct sockaddr 397 * @param sa2 (r) pointer to an struct sockaddr 398 * 399 * @returns Addresses are converted to strings and compared with strcmp and 400 * the result of strcmp is returned. 401 * 402 * @note IPv6 mapped IPv4 addresses are treated as IPv4 addresses. 403 */ 404int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2) 405{ 406 int ret; 407 char *ip1; 408 const char *ip2; 409 410 ip1 = strdup(getip_string(sa1)); 411 ip2 = getip_string(sa2); 412 413 ret = strcmp(ip1, ip2); 414 415 free(ip1); 416 417 return ret; 418} 419 420#define POLL_FD_SET_STARTSIZE 512 421#define POLL_FD_SET_INCREASE 128 422/*! 423 * Add a fd to a dynamic pollfd array that is allocated and grown as needed 424 * 425 * This uses an additional array of struct polldata which stores type information 426 * (enum fdtype) and a pointer to anciliary user data. 427 * 428 * 1. Allocate the arrays with an intial size of [POLL_FD_SET_STARTSIZE] if 429 * *fdsetp is NULL. 430 * 2. Grow array as needed 431 * 3. Fill in both array elements and increase count of used elements 432 * 433 * @param fdsetp (rw) pointer to callers pointer to the pollfd array 434 * @param polldatap (rw) pointer to callers pointer to the polldata array 435 * @param fdset_usedp (rw) pointer to an int with the number of used elements 436 * @param fdset_sizep (rw) pointer to an int which stores the array sizes 437 * @param fd (r) file descriptor to add to the arrays 438 * @param fdtype (r) type of fd, currently IPC_FD or LISTEN_FD 439 * @param data (rw) pointer to data the caller want to associate with an fd 440 */ 441void fdset_add_fd(struct pollfd **fdsetp, 442 struct polldata **polldatap, 443 int *fdset_usedp, 444 int *fdset_sizep, 445 int fd, 446 enum fdtype fdtype, 447 void *data) 448{ 449 struct pollfd *fdset = *fdsetp; 450 struct polldata *polldata = *polldatap; 451 int fdset_size = *fdset_sizep; 452 453 LOG(log_debug, logtype_default, "fdset_add_fd: adding fd %i in slot %i", fd, *fdset_usedp); 454 455 if (fdset == NULL) { /* 1 */ 456 /* Initialize with space for 512 fds */ 457 fdset = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct pollfd)); 458 if (! fdset) 459 exit(EXITERR_SYS); 460 461 polldata = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct polldata)); 462 if (! polldata) 463 exit(EXITERR_SYS); 464 465 fdset_size = 512; 466 *fdset_sizep = fdset_size; 467 *fdsetp = fdset; 468 *polldatap = polldata; 469 } 470 471 if (*fdset_usedp >= fdset_size) { /* 2 */ 472 fdset = realloc(fdset, sizeof(struct pollfd) * (fdset_size + POLL_FD_SET_INCREASE)); 473 if (fdset == NULL) 474 exit(EXITERR_SYS); 475 476 polldata = realloc(polldata, sizeof(struct polldata) * (fdset_size + POLL_FD_SET_INCREASE)); 477 if (polldata == NULL) 478 exit(EXITERR_SYS); 479 480 fdset_size += POLL_FD_SET_INCREASE; 481 *fdset_sizep = fdset_size; 482 *fdsetp = fdset; 483 *polldatap = polldata; 484 } 485 486 /* 3 */ 487 fdset[*fdset_usedp].fd = fd; 488 fdset[*fdset_usedp].events = POLLIN; 489 polldata[*fdset_usedp].fdtype = fdtype; 490 polldata[*fdset_usedp].data = data; 491 (*fdset_usedp)++; 492} 493 494/*! 495 * Remove a fd from our pollfd array 496 * 497 * 1. Search fd 498 * 2a 499 * 2b If we remove the last array elemnt, just decrease count 500 * 3. If found move all following elements down by one 501 * 4. Decrease count of used elements in array 502 * 503 * This currently doesn't shrink the allocated storage of the array. 504 * 505 * @param fdsetp (rw) pointer to callers pointer to the pollfd array 506 * @param polldatap (rw) pointer to callers pointer to the polldata array 507 * @param fdset_usedp (rw) pointer to an int with the number of used elements 508 * @param fdset_sizep (rw) pointer to an int which stores the array sizes 509 * @param fd (r) file descriptor to remove from the arrays 510 */ 511void fdset_del_fd(struct pollfd **fdsetp, 512 struct polldata **polldatap, 513 int *fdset_usedp, 514 int *fdset_sizep _U_, 515 int fd) 516{ 517 struct pollfd *fdset = *fdsetp; 518 struct polldata *polldata = *polldatap; 519 int i; 520 521 if (*fdset_usedp < 1) 522 return; 523 524 for (i = 0; i < *fdset_usedp; i++) { 525 if (fdset[i].fd == fd) { /* 1 */ 526 if (i == 0 && *fdset_usedp == 1) { /* 2a */ 527 fdset[i].fd = -1; 528 memset(&polldata[i], 0, sizeof(struct polldata)); 529 } else if (i < (*fdset_usedp - 1)) { /* 2b */ 530 memmove(&fdset[i], &fdset[i+1], (*fdset_usedp - 1) * sizeof(struct pollfd)); /* 3 */ 531 memmove(&polldata[i], &polldata[i+1], (*fdset_usedp - 1) * sizeof(struct polldata)); /* 3 */ 532 } 533 (*fdset_usedp)--; 534 break; 535 } 536 } 537} 538 539/* Length of the space taken up by a padded control message of length len */ 540#ifndef CMSG_SPACE 541#define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len)) 542#endif 543 544/* 545 * Receive a fd on a suitable socket 546 * @args fd (r) PF_UNIX socket to receive on 547 * @args nonblocking (r) 0: fd is in blocking mode - 1: fd is nonblocking, poll for 1 sec 548 * @returns fd on success, -1 on error 549 */ 550int recv_fd(int fd, int nonblocking) 551{ 552 int ret; 553 struct msghdr msgh; 554 struct iovec iov[1]; 555 struct cmsghdr *cmsgp = NULL; 556 char buf[CMSG_SPACE(sizeof(int))]; 557 char dbuf[80]; 558 struct pollfd pollfds[1]; 559 560 pollfds[0].fd = fd; 561 pollfds[0].events = POLLIN; 562 563 memset(&msgh,0,sizeof(msgh)); 564 memset(buf,0,sizeof(buf)); 565 566 msgh.msg_name = NULL; 567 msgh.msg_namelen = 0; 568 569 msgh.msg_iov = iov; 570 msgh.msg_iovlen = 1; 571 572 iov[0].iov_base = dbuf; 573 iov[0].iov_len = sizeof(dbuf); 574 575 msgh.msg_control = buf; 576 msgh.msg_controllen = sizeof(buf); 577 578 if (nonblocking) { 579 do { 580 ret = poll(pollfds, 1, 2000); /* poll 2 seconds, evtl. multipe times (EINTR) */ 581 } while ( ret == -1 && errno == EINTR ); 582 if (ret != 1) 583 return -1; 584 ret = recvmsg(fd, &msgh, 0); 585 } else { 586 do { 587 ret = recvmsg(fd, &msgh, 0); 588 } while ( ret == -1 && errno == EINTR ); 589 } 590 591 if ( ret == -1 ) { 592 return -1; 593 } 594 595 for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) { 596 if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) { 597 return *(int *) CMSG_DATA(cmsgp); 598 } 599 } 600 601 if ( ret == sizeof (int) ) 602 errno = *(int *)dbuf; /* Rcvd errno */ 603 else 604 errno = ENOENT; /* Default errno */ 605 606 return -1; 607} 608 609/* 610 * Send a fd across a suitable socket 611 */ 612int send_fd(int socket, int fd) 613{ 614 int ret; 615 struct msghdr msgh; 616 struct iovec iov[1]; 617 struct cmsghdr *cmsgp = NULL; 618 char *buf; 619 size_t size; 620 int er=0; 621 622 size = CMSG_SPACE(sizeof fd); 623 buf = malloc(size); 624 if (!buf) { 625 LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno)); 626 return -1; 627 } 628 629 memset(&msgh,0,sizeof (msgh)); 630 memset(buf,0, size); 631 632 msgh.msg_name = NULL; 633 msgh.msg_namelen = 0; 634 635 msgh.msg_iov = iov; 636 msgh.msg_iovlen = 1; 637 638 iov[0].iov_base = &er; 639 iov[0].iov_len = sizeof(er); 640 641 msgh.msg_control = buf; 642 msgh.msg_controllen = size; 643 644 cmsgp = CMSG_FIRSTHDR(&msgh); 645 cmsgp->cmsg_level = SOL_SOCKET; 646 cmsgp->cmsg_type = SCM_RIGHTS; 647 cmsgp->cmsg_len = CMSG_LEN(sizeof(fd)); 648 649 *((int *)CMSG_DATA(cmsgp)) = fd; 650 msgh.msg_controllen = cmsgp->cmsg_len; 651 652 do { 653 ret = sendmsg(socket,&msgh, 0); 654 } while ( ret == -1 && errno == EINTR ); 655 if (ret == -1) { 656 LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno)); 657 free(buf); 658 return -1; 659 } 660 free(buf); 661 return 0; 662} 663