2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
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.
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.
17 * Netatalk utility functions
22 #endif /* HAVE_CONFIG_H */
25 # define _XOPEN_SOURCE 600
27 #ifndef __EXTENSIONS__
28 # define __EXTENSIONS__
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
44 #include <sys/ioctl.h>
46 #include <atalk/logger.h>
47 #include <atalk/util.h>
49 static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
52 * @brief set or unset non-blocking IO on a fd
54 * @param fd (r) File descriptor
55 * @param cmd (r) 0: disable non-blocking IO, ie block\n
56 * <>0: enable non-blocking IO
58 * @returns 0 on success, -1 on failure
60 int setnonblock(int fd, int cmd)
65 if ((fdflags = ofdflags = fcntl(fd, F_GETFL, 0)) == -1)
69 fdflags |= O_NONBLOCK;
71 fdflags &= ~O_NONBLOCK;
73 if (fdflags != ofdflags)
74 if (fcntl(fd, F_SETFL, fdflags) == -1)
81 * non-blocking drop-in replacement for read with timeout using select
83 * @param socket (r) socket, if in blocking mode, pass "setnonblocking" arg as 1
84 * @param data (rw) buffer for the read data
85 * @param lenght (r) how many bytes to read
86 * @param setnonblocking (r) when non-zero this func will enable and disable non blocking
87 * io mode for the socket
88 * @param timeout (r) number of seconds to try reading
90 * @returns number of bytes actually read or -1 on fatal error
92 ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, int timeout)
96 struct timeval now, end, tv;
100 if (setnonblocking) {
101 if (setnonblock(socket, 1) != 0)
105 /* Calculate end time */
106 (void)gettimeofday(&now, NULL);
108 end.tv_sec += timeout;
110 while (stored < length) {
111 len = read(socket, (char *) data + stored, length - stored);
118 FD_SET(socket, &rfds);
122 while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
125 LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
129 if (errno == EINTR) {
130 (void)gettimeofday(&now, NULL);
131 if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
132 LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
135 if (now.tv_usec > end.tv_usec) {
136 tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
137 tv.tv_sec = end.tv_sec - now.tv_sec - 1;
139 tv.tv_usec = end.tv_usec - now.tv_usec;
140 tv.tv_sec = end.tv_sec - now.tv_sec;
143 FD_SET(socket, &rfds);
146 LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
150 } /* while (select) */
152 } /* switch (errno) */
153 LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
161 } /* while (stored < length) */
164 if (setnonblocking) {
165 if (setnonblock(socket, 0) != 0)
169 if (len == -1 && stored == 0)
170 /* last read or select got an error and we haven't got yet anything => return -1*/
176 * non-blocking drop-in replacement for read with timeout using select
178 * @param socket (r) socket, if in blocking mode, pass "setnonblocking" arg as 1
179 * @param data (rw) buffer for the read data
180 * @param lenght (r) how many bytes to read
181 * @param setnonblocking (r) when non-zero this func will enable and disable non blocking
182 * io mode for the socket
183 * @param timeout (r) number of seconds to try reading
185 * @returns number of bytes actually read or -1 on fatal error
187 ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout)
191 struct timeval now, end, tv;
195 if (setnonblocking) {
196 if (setnonblock(socket, 1) != 0)
200 /* Calculate end time */
201 (void)gettimeofday(&now, NULL);
203 end.tv_sec += timeout;
205 while (stored < length) {
206 len = write(socket, (char *) data + stored, length - stored);
213 FD_SET(socket, &rfds);
217 while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
220 LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
224 if (errno == EINTR) {
225 (void)gettimeofday(&now, NULL);
226 if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
227 LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
230 if (now.tv_usec > end.tv_usec) {
231 tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
232 tv.tv_sec = end.tv_sec - now.tv_sec - 1;
234 tv.tv_usec = end.tv_usec - now.tv_usec;
235 tv.tv_sec = end.tv_sec - now.tv_sec;
238 FD_SET(socket, &rfds);
241 LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
245 } /* while (select) */
247 } /* switch (errno) */
248 LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
256 } /* while (stored < length) */
259 if (setnonblocking) {
260 if (setnonblock(socket, 0) != 0)
264 if (len == -1 && stored == 0)
265 /* last read or select got an error and we haven't got yet anything => return -1*/
271 * @brief convert an IPv4 or IPv6 address to a static string using inet_ntop
273 * IPv6 mapped IPv4 addresses are returned as IPv4 addreses eg
274 * ::ffff:10.0.0.0 is returned as "10.0.0.0".
276 * @param sa (r) pointer to an struct sockaddr
278 * @returns pointer to a static string cotaining the converted address as string.\n
279 * On error pointers to "0.0.0.0" or "::0" are returned.
281 const char *getip_string(const struct sockaddr *sa)
283 static char ip4[INET_ADDRSTRLEN];
284 static char ip6[INET6_ADDRSTRLEN];
286 switch (sa->sa_family) {
289 const struct sockaddr_in *sai4 = (const struct sockaddr_in *)sa;
290 if ((inet_ntop(AF_INET, &(sai4->sin_addr), ip4, INET_ADDRSTRLEN)) == NULL)
295 const struct sockaddr_in6 *sai6 = (const struct sockaddr_in6 *)sa;
296 if ((inet_ntop(AF_INET6, &(sai6->sin6_addr), ip6, INET6_ADDRSTRLEN)) == NULL)
299 /* Deal with IPv6 mapped IPv4 addresses*/
300 if ((memcmp(sai6->sin6_addr.s6_addr, ipv4mapprefix, sizeof(ipv4mapprefix))) == 0)
301 return (strrchr(ip6, ':') + 1);
305 return "getip_string ERROR";
308 /* We never get here */
312 * @brief return port number from struct sockaddr
314 * @param sa (r) pointer to an struct sockaddr
316 * @returns port as unsigned int
318 unsigned int getip_port(const struct sockaddr *sa)
320 if (sa->sa_family == AF_INET) { /* IPv4 */
321 const struct sockaddr_in *sai4 = (const struct sockaddr_in *)sa;
322 return ntohs(sai4->sin_port);
324 const struct sockaddr_in6 *sai6 = (const struct sockaddr_in6 *)sa;
325 return ntohs(sai6->sin6_port);
328 /* We never get here */
332 * @brief apply netmask to IP (v4 or v6)
334 * Modifies IP address in sa->sin[6]_addr-s[6]_addr. The caller is responsible
335 * for passing a value for mask that is sensible to the passed address,
336 * eg 0 <= mask <= 32 for IPv4 or 0<= mask <= 128 for IPv6. mask > 32 for
337 * IPv4 is treated as mask = 32, mask > 128 is set to 128 for IPv6.
339 * @param ai (rw) pointer to an struct sockaddr
340 * @parma mask (r) number of maskbits
342 void apply_ip_mask(struct sockaddr *sa, int mask)
345 switch (sa->sa_family) {
350 struct sockaddr_in *si = (struct sockaddr_in *)sa;
351 uint32_t nmask = mask ? ~((1 << (32 - mask)) - 1) : 0;
352 si->sin_addr.s_addr &= htonl(nmask);
359 int i, maskbytes, maskbits;
360 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)sa;
362 /* Deal with IPv6 mapped IPv4 addresses*/
363 if ((memcmp(si6->sin6_addr.s6_addr, ipv4mapprefix, sizeof(ipv4mapprefix))) == 0) {
369 maskbytes = (128 - mask) / 8; /* maskbytes really are those that will be 0'ed */
372 for (i = maskbytes - 1; i >= 0; i--)
373 si6->sin6_addr.s6_addr[15 - i] = 0;
375 si6->sin6_addr.s6_addr[15 - maskbytes] &= ~((1 << (8 - maskbits)) - 1);
384 * @brief compare IP addresses for equality
386 * @param sa1 (r) pointer to an struct sockaddr
387 * @param sa2 (r) pointer to an struct sockaddr
389 * @returns Addresses are converted to strings and compared with strcmp and
390 * the result of strcmp is returned.
392 * @note IPv6 mapped IPv4 addresses are treated as IPv4 addresses.
394 int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2)
400 ip1 = strdup(getip_string(sa1));
401 ip2 = getip_string(sa2);
403 ret = strcmp(ip1, ip2);
410 #define POLL_FD_SET_STARTSIZE 512
411 #define POLL_FD_SET_INCREASE 128
413 * Add a fd to a dynamic pollfd array that is allocated and grown as needed
415 * This uses an additional array of struct polldata which stores type information
416 * (enum fdtype) and a pointer to anciliary user data.
418 * 1. Allocate the arrays with an intial size of [POLL_FD_SET_STARTSIZE] if
420 * 2. Grow array as needed
421 * 3. Fill in both array elements and increase count of used elements
423 * @param fdsetp (rw) pointer to callers pointer to the pollfd array
424 * @param polldatap (rw) pointer to callers pointer to the polldata array
425 * @param fdset_usedp (rw) pointer to an int with the number of used elements
426 * @param fdset_sizep (rw) pointer to an int which stores the array sizes
427 * @param fd (r) file descriptor to add to the arrays
428 * @param fdtype (r) type of fd, currently IPC_FD or LISTEN_FD
429 * @param data (rw) pointer to data the caller want to associate with an fd
431 void fdset_add_fd(struct pollfd **fdsetp,
432 struct polldata **polldatap,
439 struct pollfd *fdset = *fdsetp;
440 struct polldata *polldata = *polldatap;
441 int fdset_size = *fdset_sizep;
443 LOG(log_debug, logtype_default, "fdset_add_fd: adding fd %i in slot %i", fd, *fdset_usedp);
445 if (fdset == NULL) { /* 1 */
446 /* Initialize with space for 512 fds */
447 fdset = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct pollfd));
451 polldata = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct polldata));
456 *fdset_sizep = fdset_size;
458 *polldatap = polldata;
461 if (*fdset_usedp >= fdset_size) { /* 2 */
462 fdset = realloc(fdset, sizeof(struct pollfd) * (fdset_size + POLL_FD_SET_INCREASE));
466 polldata = realloc(polldata, sizeof(struct polldata) * (fdset_size + POLL_FD_SET_INCREASE));
467 if (polldata == NULL)
470 fdset_size += POLL_FD_SET_INCREASE;
471 *fdset_sizep = fdset_size;
473 *polldatap = polldata;
477 fdset[*fdset_usedp].fd = fd;
478 fdset[*fdset_usedp].events = POLLIN;
479 polldata[*fdset_usedp].fdtype = fdtype;
480 polldata[*fdset_usedp].data = data;
485 * Remove a fd from our pollfd array
488 * 2. If we remove the last array elemnt, just decrease count
489 * 3. If found move all following elements down by one
490 * 4. Decrease count of used elements in array
492 * This currently doesn't shrink the allocated storage of the array.
494 * @param fdsetp (rw) pointer to callers pointer to the pollfd array
495 * @param polldatap (rw) pointer to callers pointer to the polldata array
496 * @param fdset_usedp (rw) pointer to an int with the number of used elements
497 * @param fdset_sizep (rw) pointer to an int which stores the array sizes
498 * @param fd (r) file descriptor to remove from the arrays
500 void fdset_del_fd(struct pollfd **fdsetp,
501 struct polldata **polldatap,
503 int *fdset_sizep _U_,
506 struct pollfd *fdset = *fdsetp;
507 struct polldata *polldata = *polldatap;
509 for (int i = 0; i < *fdset_usedp; i++) {
510 if (fdset[i].fd == fd) { /* 1 */
511 if (i < (*fdset_usedp - 1)) { /* 2 */
512 memmove(&fdset[i], &fdset[i+1], (*fdset_usedp - 1) * sizeof(struct pollfd)); /* 3 */
513 memmove(&polldata[i], &polldata[i+1], (*fdset_usedp - 1) * sizeof(struct polldata)); /* 3 */
521 /* Length of the space taken up by a padded control message of length len */
523 #define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len))
527 * Receive a fd on a suitable socket
528 * @args fd (r) PF_UNIX socket to receive on
529 * @args nonblocking (r) 0: fd is in blocking mode - 1: fd is nonblocking, poll for 1 sec
530 * @returns fd on success, -1 on error
532 int recv_fd(int fd, int nonblocking)
537 struct cmsghdr *cmsgp = NULL;
538 char buf[CMSG_SPACE(sizeof(int))];
540 struct pollfd pollfds[1];
543 pollfds[0].events = POLLIN;
545 memset(&msgh,0,sizeof(msgh));
546 memset(buf,0,sizeof(buf));
548 msgh.msg_name = NULL;
549 msgh.msg_namelen = 0;
554 iov[0].iov_base = dbuf;
555 iov[0].iov_len = sizeof(dbuf);
557 msgh.msg_control = buf;
558 msgh.msg_controllen = sizeof(buf);
562 ret = poll(pollfds, 1, 2000); /* poll 2 seconds, evtl. multipe times (EINTR) */
563 } while ( ret == -1 && errno == EINTR );
566 ret = recvmsg(fd, &msgh, 0);
569 ret = recvmsg(fd, &msgh, 0);
570 } while ( ret == -1 && errno == EINTR );
577 for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
578 if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
579 return *(int *) CMSG_DATA(cmsgp);
583 if ( ret == sizeof (int) )
584 errno = *(int *)dbuf; /* Rcvd errno */
586 errno = ENOENT; /* Default errno */
592 * Send a fd across a suitable socket
594 int send_fd(int socket, int fd)
599 struct cmsghdr *cmsgp = NULL;
604 size = CMSG_SPACE(sizeof fd);
607 LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
611 memset(&msgh,0,sizeof (msgh));
614 msgh.msg_name = NULL;
615 msgh.msg_namelen = 0;
620 iov[0].iov_base = &er;
621 iov[0].iov_len = sizeof(er);
623 msgh.msg_control = buf;
624 msgh.msg_controllen = size;
626 cmsgp = CMSG_FIRSTHDR(&msgh);
627 cmsgp->cmsg_level = SOL_SOCKET;
628 cmsgp->cmsg_type = SCM_RIGHTS;
629 cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
631 *((int *)CMSG_DATA(cmsgp)) = fd;
632 msgh.msg_controllen = cmsgp->cmsg_len;
635 ret = sendmsg(socket,&msgh, 0);
636 } while ( ret == -1 && errno == EINTR );
638 LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));