netatalk  4.6.1
Free and Open Source Apple Filing Protocol (AFP) Server
Loading...
Searching...
No Matches
socket.c
Go to the documentation of this file.
1/*
2 Copyright (c) 2009 Frank Lahm <[email protected]>
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
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif /* HAVE_CONFIG_H */
23
24#include <arpa/inet.h>
25#include <errno.h>
26#include <fcntl.h>
27#include <netinet/in.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/ioctl.h>
31#include <sys/socket.h>
32#include <sys/time.h>
33#include <sys/types.h>
34#include <sys/uio.h>
35#include <time.h>
36#include <unistd.h>
37
38#include <atalk/errchk.h>
39#include <atalk/logger.h>
40#include <atalk/util.h>
41
42static char ipv4mapprefix[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
43
53int setnonblock(int fd, int cmd)
54{
55 int ofdflags;
56 int fdflags;
57
58 if ((fdflags = ofdflags = fcntl(fd, F_GETFL, 0)) == -1) {
59 return -1;
60 }
61
62 if (cmd) {
63 fdflags |= O_NONBLOCK;
64 } else {
65 fdflags &= ~O_NONBLOCK;
66 }
67
68 if (fdflags != ofdflags)
69 if (fcntl(fd, F_SETFL, fdflags) == -1) {
70 return -1;
71 }
72
73 return 0;
74}
75
88ssize_t readt(int socket, void *data, const size_t length, int setnonblocking,
89 int timeout)
90{
91 size_t stored = 0;
92 ssize_t len = 0;
93 struct timeval now, end, tv;
94 fd_set rfds;
95 int ret;
96 FD_ZERO(&rfds);
97
98 if (setnonblocking) {
99 if (setnonblock(socket, 1) != 0) {
100 return -1;
101 }
102 }
103
104 /* Calculate end time */
105 if (timeout) {
106 (void)gettimeofday(&now, NULL);
107 end = now;
108 end.tv_sec += timeout;
109 }
110
111 while (stored < length) {
112 len = recv(socket, (char *) data + stored, length - stored, 0);
113
114 if (len == -1) {
115 switch (errno) {
116 case EINTR:
117 continue;
118
119 case EAGAIN:
120 if (socket >= FD_SETSIZE) {
121 errno = EINVAL;
122 goto exit;
123 }
124
125 FD_SET(socket, &rfds);
126
127 if (timeout) {
128 tv.tv_usec = 0;
129 tv.tv_sec = timeout;
130 }
131
132 while ((ret = select(socket + 1, &rfds, NULL, NULL,
133 timeout ? &tv : NULL)) < 1) {
134 switch (ret) {
135 case 0:
136 LOG(log_debug, logtype_dsi, "select timeout %d s", timeout);
137 errno = EAGAIN;
138 goto exit;
139
140 default: /* -1 */
141 switch (errno) {
142 case EINTR:
143 if (timeout) {
144 (void)gettimeofday(&now, NULL);
145
146 if (now.tv_sec > end.tv_sec
147 ||
148 (now.tv_sec == end.tv_sec && now.tv_usec >= end.tv_usec)) {
149 LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
150 goto exit;
151 }
152
153 if (now.tv_usec > end.tv_usec) {
154 tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
155 tv.tv_sec = end.tv_sec - now.tv_sec - 1;
156 } else {
157 tv.tv_usec = end.tv_usec - now.tv_usec;
158 tv.tv_sec = end.tv_sec - now.tv_sec;
159 }
160 }
161
162 FD_SET(socket, &rfds);
163 continue;
164
165 case EBADF:
166 /* possibly entered disconnected state, don't spam log here */
167 LOG(log_debug, logtype_afpd, "select: %s", strerror(errno));
168 stored = -1;
169 goto exit;
170
171 default:
172 LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
173 stored = -1;
174 goto exit;
175 }
176 }
177 } /* while (select) */
178
179 continue;
180 } /* switch (errno) */
181
182 LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
183 stored = -1;
184 goto exit;
185 } /* (len == -1) */
186 else if (len > 0) {
187 stored += len;
188 } else {
189 break;
190 }
191 } /* while (stored < length) */
192
193exit:
194
195 if (setnonblocking) {
196 if (setnonblock(socket, 0) != 0) {
197 return -1;
198 }
199 }
200
201 if (len == -1 && stored == 0)
202 /* last read or select got an error and we haven't got yet anything => return -1*/
203 {
204 return -1;
205 }
206
207 return stored;
208}
209
222ssize_t writet(int socket, void *data, const size_t length, int setnonblocking,
223 int timeout)
224{
225 size_t stored = 0;
226 ssize_t len = 0;
227 struct timeval now, end, tv;
228 fd_set rfds;
229 int ret;
230
231 if (setnonblocking) {
232 if (setnonblock(socket, 1) != 0) {
233 return -1;
234 }
235 }
236
237 /* Calculate end time */
238 (void)gettimeofday(&now, NULL);
239 end = now;
240 end.tv_sec += timeout;
241
242 while (stored < length) {
243 len = write(socket, (char *) data + stored, length - stored);
244
245 if (len == -1) {
246 switch (errno) {
247 case EINTR:
248 continue;
249
250 case EAGAIN:
251 if (socket >= FD_SETSIZE) {
252 errno = EINVAL;
253 goto exit;
254 }
255
256 FD_ZERO(&rfds);
257 FD_SET(socket, &rfds);
258 tv.tv_usec = 0;
259 tv.tv_sec = timeout;
260
261 while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
262 switch (ret) {
263 case 0:
264 LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
265 goto exit;
266
267 default: /* -1 */
268 if (errno == EINTR) {
269 (void)gettimeofday(&now, NULL);
270
271 if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
272 LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
273 goto exit;
274 }
275
276 if (now.tv_usec > end.tv_usec) {
277 tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
278 tv.tv_sec = end.tv_sec - now.tv_sec - 1;
279 } else {
280 tv.tv_usec = end.tv_usec - now.tv_usec;
281 tv.tv_sec = end.tv_sec - now.tv_sec;
282 }
283
284 FD_ZERO(&rfds);
285 FD_SET(socket, &rfds);
286 continue;
287 }
288
289 LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
290 stored = -1;
291 goto exit;
292 }
293 } /* while (select) */
294
295 continue;
296 } /* switch (errno) */
297
298 LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
299 stored = -1;
300 goto exit;
301 } /* (len == -1) */
302 else if (len > 0) {
303 stored += len;
304 } else {
305 break;
306 }
307 } /* while (stored < length) */
308
309exit:
310
311 if (setnonblocking) {
312 if (setnonblock(socket, 0) != 0) {
313 return -1;
314 }
315 }
316
317 if (len == -1 && stored == 0)
318 /* last read or select got an error and we haven't got yet anything => return -1*/
319 {
320 return -1;
321 }
322
323 return stored;
324}
325
337const char *getip_string(const struct sockaddr *sa)
338{
339 static char ip4[INET_ADDRSTRLEN];
340 static char ip6[INET6_ADDRSTRLEN];
341
342 switch (sa->sa_family) {
343 case AF_INET: {
344 const struct sockaddr_in *sai4 = (const struct sockaddr_in *)sa;
345
346 if ((inet_ntop(AF_INET, &(sai4->sin_addr), ip4, INET_ADDRSTRLEN)) == NULL) {
347 return "0.0.0.0";
348 }
349
350 return ip4;
351 }
352
353 case AF_INET6: {
354 const struct sockaddr_in6 *sai6 = (const struct sockaddr_in6 *)sa;
355
356 if ((inet_ntop(AF_INET6, &(sai6->sin6_addr), ip6, INET6_ADDRSTRLEN)) == NULL) {
357 return "::0";
358 }
359
360 /* Deal with IPv6 mapped IPv4 addresses*/
361 if ((memcmp(sai6->sin6_addr.s6_addr, ipv4mapprefix,
362 sizeof(ipv4mapprefix))) == 0) {
363 return strrchr(ip6, ':') + 1;
364 }
365
366 return ip6;
367 }
368
369 default:
370 return "getip_string ERROR";
371 }
372
373 /* We never get here */
374}
375
383unsigned int getip_port(const struct sockaddr *sa)
384{
385 if (sa->sa_family == AF_INET) { /* IPv4 */
386 const struct sockaddr_in *sai4 = (const struct sockaddr_in *)sa;
387 return ntohs(sai4->sin_port);
388 } else { /* IPv6 */
389 const struct sockaddr_in6 *sai6 = (const struct sockaddr_in6 *)sa;
390 return ntohs(sai6->sin6_port);
391 }
392
393 /* We never get here */
394}
395
407void apply_ip_mask(struct sockaddr *sa, int mask)
408{
409 switch (sa->sa_family) {
410 case AF_INET: {
411 if (mask >= 32) {
412 return;
413 }
414
415 struct sockaddr_in *si = (struct sockaddr_in *)sa;
416
417 uint32_t nmask = mask ? ~((1 << (32 - mask)) - 1) : 0;
418 si->sin_addr.s_addr &= htonl(nmask);
419 break;
420 }
421
422 case AF_INET6: {
423 if (mask >= 128) {
424 return;
425 }
426
427 int i, maskbytes, maskbits;
428 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)sa;
429
430 /* Deal with IPv6 mapped IPv4 addresses*/
431 if ((memcmp(si6->sin6_addr.s6_addr, ipv4mapprefix,
432 sizeof(ipv4mapprefix))) == 0) {
433 mask += 96;
434
435 if (mask >= 128) {
436 return;
437 }
438 }
439
440 maskbytes = (128 - mask) / 8; /* maskbytes really are those that will be 0'ed */
441 maskbits = mask % 8;
442
443 for (i = maskbytes - 1; i >= 0; i--) {
444 si6->sin6_addr.s6_addr[15 - i] = 0;
445 }
446
447 if (maskbits) {
448 si6->sin6_addr.s6_addr[15 - maskbytes] &= ~((1 << (8 - maskbits)) - 1);
449 }
450
451 break;
452 }
453
454 default:
455 break;
456 }
457}
458
470int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2)
471{
472 int ret;
473 char *ip1;
474 const char *ip2;
475 ip1 = strdup(getip_string(sa1));
476 ip2 = getip_string(sa2);
477 ret = strcmp(ip1, ip2);
478 free(ip1);
479 return ret;
480}
481
502int tokenize_ip_port(const char *ipurl, char **address, char **port)
503{
504 EC_INIT;
505 char *p = NULL;
506 char *s;
507 AFP_ASSERT(ipurl && address && port);
508 EC_NULL(p = strdup(ipurl));
509
510 /* Either ipv4, ipv4:port, ipv6, [ipv6] or [ipv6]:port */
511
512 if (!strchr(p, ':')) {
513 /* IPv4 address without port */
514 *address = p;
515 p = NULL; /* prevent free() */
516 *port = NULL;
518 }
519
520 /* Either ipv4:port, ipv6, [ipv6] or [ipv6]:port */
521
522 if (strchr(p, '.')) {
523 /* ipv4:port */
524 *address = p;
525 p = strchr(p, ':');
526 *p = '\0';
527 EC_NULL(*port = strdup(p + 1));
528 p = NULL; /* prevent free() */
530 }
531
532 /* Either ipv6, [ipv6] or [ipv6]:port */
533
534 if (p[0] != '[') {
535 /* ipv6 */
536 *address = p;
537 p = NULL; /* prevent free() */
538 *port = NULL;
540 }
541
542 /* [ipv6] or [ipv6]:port */
543 EC_NULL(*address = strdup(p + 1));
544
545 if ((s = strchr(*address, ']')) == NULL) {
546 LOG(log_error, logtype_dsi, "tokenize_ip_port: malformed ipv6 address %s\n",
547 ipurl);
548 EC_FAIL;
549 }
550
551 *s = '\0';
552 /* address now points to the ipv6 address without [] */
553
554 if (s[1] == ':') {
555 /* [ipv6]:port */
556 EC_NULL(*port = strdup(s + 2));
557 } else {
558 /* [ipv6] */
559 *port = NULL;
560 }
561
563
564 if (p) {
565 free(p);
566 }
567
568 EC_EXIT;
569}
570
574struct asev *asev_init(int max)
575{
576 struct asev *asev = calloc(1, sizeof(struct asev));
577
578 if (asev == NULL) {
579 return NULL;
580 }
581
582 /* Initialize with space for all possibly active fds */
583 asev->fdset = calloc(max, sizeof(struct pollfd));
584 asev->data = calloc(max, sizeof(struct asev_data));
585
586 if (asev->fdset == NULL || asev->data == NULL) {
587 free(asev->fdset);
588 free(asev->data);
589 free(asev);
590 return NULL;
591 }
592
593 asev->max = max;
594 asev->used = 0;
595 return asev;
596}
597
604bool asev_add_fd(struct asev *asev,
605 int fd,
606 enum asev_fdtype fdtype,
607 void *private,
608 int protocol)
609{
610 if (asev == NULL) {
611 return false;
612 }
613
614 if (!(asev->used < asev->max)) {
615 return false;
616 }
617
618 asev->fdset[asev->used].fd = fd;
619 asev->fdset[asev->used].events = POLLIN;
620 asev->data[asev->used].fdtype = fdtype;
621 asev->data[asev->used].private = private;
622 asev->data[asev->used].protocol = protocol;
623 asev->used++;
624 return true;
625}
626
632bool asev_del_fd(struct asev *asev, int fd)
633{
634 int i;
635 int numafter;
636
637 if (asev == NULL) {
638 return false;
639 }
640
641 if (asev->used == 0) {
642 LOG(log_error, logtype_cnid, "asev_del_fd: empty");
643 return false;
644 }
645
646 for (i = 0; i < asev->used; i++) {
647 /*
648 * Scan the array for a matching fd
649 */
650 if (asev->fdset[i].fd == fd) {
651 /*
652 * found fd
653 */
654 if ((i + 1) == asev->used) {
655 /*
656 * it's the last (or only) array element, simply null it
657 */
658 asev->fdset[i].fd = -1;
659 asev->data[i].fdtype = 0;
660 asev->data[i].private = NULL;
661 asev->data[i].protocol = 0;
662 } else {
663 /*
664 * Move down by one all subsequent elements
665 */
666 numafter = asev->used - (i + 1);
667 memmove(&asev->fdset[i], &asev->fdset[i + 1],
668 numafter * sizeof(struct pollfd));
669 memmove(&asev->data[i], &asev->data[i + 1],
670 numafter * sizeof(struct asev_data));
671 }
672
673 asev->used--;
674 return true;
675 }
676 }
677
678 return false;
679}
680
681#ifndef CMSG_SPACE
683#define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len))
684#endif
685
692int recv_fd(int fd, int nonblocking)
693{
694 int ret;
695 struct msghdr msgh;
696 struct iovec iov[1];
697 struct cmsghdr *cmsgp = NULL;
698 char buf[CMSG_SPACE(sizeof(int))];
699 char dbuf[80];
700 struct pollfd pollfds[1];
701 pollfds[0].fd = fd;
702 pollfds[0].events = POLLIN;
703 memset(&msgh, 0, sizeof(msgh));
704 memset(buf, 0, sizeof(buf));
705 msgh.msg_name = NULL;
706 msgh.msg_namelen = 0;
707 msgh.msg_iov = iov;
708 msgh.msg_iovlen = 1;
709 iov[0].iov_base = dbuf;
710 iov[0].iov_len = sizeof(dbuf);
711 msgh.msg_control = buf;
712 msgh.msg_controllen = sizeof(buf);
713
714 if (nonblocking) {
715 do {
716 /* poll 2 seconds, evtl. multipe times (EINTR) */
717 ret = poll(pollfds, 1, 2000);
718 } while (ret == -1 && errno == EINTR);
719
720 if (ret != 1) {
721 return -1;
722 }
723
724 ret = recvmsg(fd, &msgh, 0);
725 } else {
726 do {
727 ret = recvmsg(fd, &msgh, 0);
728 } while (ret == -1 && errno == EINTR);
729 }
730
731 if (ret == -1) {
732 return -1;
733 }
734
735 for (cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL;
736 cmsgp = CMSG_NXTHDR(&msgh, cmsgp)) {
737 if (cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS) {
738 return *(int *) CMSG_DATA(cmsgp);
739 }
740 }
741
742 if (ret == sizeof(int)) {
743 /* Rcvd errno */
744 errno = *(int *)dbuf;
745 } else {
746 /* Default errno */
747 errno = ENOENT;
748 }
749
750 return -1;
751}
752
756int send_fd(int socket, int fd)
757{
758 int ret;
759 struct msghdr msgh;
760 struct iovec iov[1];
761 struct cmsghdr *cmsgp = NULL;
762 char *buf;
763 size_t size;
764 int er = 0;
765 size = CMSG_SPACE(sizeof(fd));
766 buf = malloc(size);
767
768 if (!buf) {
769 LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
770 return -1;
771 }
772
773 memset(&msgh, 0, sizeof(msgh));
774 memset(buf, 0, size);
775 msgh.msg_name = NULL;
776 msgh.msg_namelen = 0;
777 msgh.msg_iov = iov;
778 msgh.msg_iovlen = 1;
779 iov[0].iov_base = &er;
780 iov[0].iov_len = sizeof(er);
781 msgh.msg_control = buf;
782 msgh.msg_controllen = size;
783 cmsgp = CMSG_FIRSTHDR(&msgh);
784 cmsgp->cmsg_level = SOL_SOCKET;
785 cmsgp->cmsg_type = SCM_RIGHTS;
786 cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
787 *((int *)CMSG_DATA(cmsgp)) = fd;
788 msgh.msg_controllen = cmsgp->cmsg_len;
789
790 do {
791 ret = sendmsg(socket, &msgh, 0);
792 } while (ret == -1 && errno == EINTR);
793
794 if (ret == -1) {
795 LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
796 free(buf);
797 return -1;
798 }
799
800 free(buf);
801 return 0;
802}
static char buf[MAXPATHLEN+1]
Definition afppasswd.c:85
static struct savedt sa
Definition appl.c:32
static struct savedt si
Definition desktop.c:307
size_t p
Definition dircache.c:306
Error checking macros.
#define EC_CLEANUP
Definition errchk.h:28
#define EC_EXIT_STATUS(a)
Definition errchk.h:20
#define EC_INIT
Definition errchk.h:18
#define EC_EXIT
Definition errchk.h:29
#define EC_NULL(a)
Definition errchk.h:168
#define EC_FAIL
Definition errchk.h:21
#define data
Definition hash.c:52
#define mask
Definition hash.c:66
Netatalk utility functions.
asev_fdtype
Definition include/atalk/util.h:187
#define AFP_ASSERT(b)
Definition include/atalk/util.h:55
#define LOG(log_level, type,...)
Definition logger.h:148
@ logtype_cnid
Definition logger.h:36
@ logtype_dsi
Definition logger.h:38
@ logtype_afpd
Definition logger.h:37
@ log_warning
Definition logger.h:21
@ log_error
Definition logger.h:20
@ log_debug
Definition logger.h:24
static long now(void)
Definition macip.c:191
static unsigned char port
Definition pap.c:534
ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, int timeout)
non-blocking drop-in replacement for read with timeout using select
Definition socket.c:88
const char * getip_string(const struct sockaddr *sa)
convert an IPv4 or IPv6 address to a static string using inet_ntop
Definition socket.c:337
static char ipv4mapprefix[]
Definition socket.c:42
int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2)
compare IP addresses for equality
Definition socket.c:470
bool asev_add_fd(struct asev *asev, int fd, enum asev_fdtype fdtype, void *private, int protocol)
Add a fd to a dynamic pollfd array and associated data array.
Definition socket.c:604
bool asev_del_fd(struct asev *asev, int fd)
Remove fd from asev.
Definition socket.c:632
int tokenize_ip_port(const char *ipurl, char **address, char **port)
Tokenize IP(4/6) addresses with an optional port into address and port.
Definition socket.c:502
#define CMSG_SPACE(len)
Definition socket.c:683
unsigned int getip_port(const struct sockaddr *sa)
return port number from struct sockaddr
Definition socket.c:383
struct asev * asev_init(int max)
Definition socket.c:574
void apply_ip_mask(struct sockaddr *sa, int mask)
apply netmask to IP (v4 or v6)
Definition socket.c:407
int send_fd(int socket, int fd)
Definition socket.c:756
ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout)
non-blocking drop-in replacement for read with timeout using select
Definition socket.c:222
int recv_fd(int fd, int nonblocking)
Receive a fd on a suitable socket.
Definition socket.c:692
int setnonblock(int fd, int cmd)
set or unset non-blocking IO on a fd
Definition socket.c:53
atalk socket event data
Definition include/atalk/util.h:192
int protocol
Definition include/atalk/util.h:196
enum asev_fdtype fdtype
Definition include/atalk/util.h:193
void * private
Definition include/atalk/util.h:194
atalk socket event
Definition include/atalk/util.h:202
int max
Definition include/atalk/util.h:205
int used
Definition include/atalk/util.h:206
struct asev_data * data
Definition include/atalk/util.h:204
struct pollfd * fdset
Definition include/atalk/util.h:203
#define fcntl
Definition test_afppasswd.c:47
#define write
Definition test_migrate.c:41
#define select
Definition test_socket_helpers.c:40
#define recv
Definition test_socket_helpers.c:39
#define CMSG_LEN(len)
Definition usockfd.h:31