• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/netatalk-3.0.5/libatalk/dsi/
1/*
2 * Copyright (c) 1997, 1998 Adrian Sun (asun@zoology.washington.edu)
3 * All rights reserved. See COPYRIGHT.
4 *
5 * this provides both proto_open() and proto_close() to account for
6 * protocol specific initialization and shutdown procedures. all the
7 * read/write stuff is done in dsi_stream.c.  */
8
9#ifdef HAVE_CONFIG_H
10#include "config.h"
11#endif /* HAVE_CONFIG_H */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include <errno.h>
18#ifdef HAVE_NETDB_H
19#include <netdb.h>
20#endif /* HAVE_NETDB_H */
21#include <sys/types.h>
22#include <sys/time.h>
23#include <sys/socket.h>
24#include <stdint.h>
25
26#include <sys/ioctl.h>
27#ifdef TRU64
28#include <sys/mbuf.h>
29#include <net/route.h>
30#endif /* TRU64 */
31#include <net/if.h>
32#include <netinet/tcp.h>
33#include <netinet/in.h>
34#include <arpa/inet.h>
35
36#include <signal.h>
37#include <atalk/logger.h>
38
39#ifdef __svr4__
40#include <sys/sockio.h>
41#endif /* __svr4__ */
42
43#ifdef TCPWRAP
44#include <tcpd.h>
45int allow_severity = log_info;
46int deny_severity = log_warning;
47#endif /* TCPWRAP */
48
49#include <atalk/dsi.h>
50#include <atalk/compat.h>
51#include <atalk/util.h>
52#include <atalk/errchk.h>
53
54#define min(a,b)  ((a) < (b) ? (a) : (b))
55
56#ifndef DSI_TCPMAXPEND
57#define DSI_TCPMAXPEND      20       /* max # of pending connections */
58#endif /* DSI_TCPMAXPEND */
59
60#ifndef DSI_TCPTIMEOUT
61#define DSI_TCPTIMEOUT      120     /* timeout in seconds for connections */
62#endif /* ! DSI_TCPTIMEOUT */
63
64
65/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
66#ifndef SOCKLEN_T
67#define SOCKLEN_T unsigned int
68#endif /* ! SOCKLEN_T */
69
70static void dsi_tcp_close(DSI *dsi)
71{
72    if (dsi->socket == -1)
73        return;
74
75    close(dsi->socket);
76    dsi->socket = -1;
77}
78
79/* alarm handler for tcp_open */
80static void timeout_handler(int sig _U_)
81{
82    LOG(log_error, logtype_dsi, "dsi_tcp_open: connection timed out");
83    exit(EXITERR_CLNT);
84}
85
86/*!
87 * Allocate DSI read buffer and read-ahead buffer
88 */
89static void dsi_init_buffer(DSI *dsi)
90{
91    if ((dsi->commands = malloc(dsi->server_quantum)) == NULL) {
92        LOG(log_error, logtype_dsi, "dsi_init_buffer: OOM");
93        AFP_PANIC("OOM in dsi_init_buffer");
94    }
95
96    /* dsi_peek() read ahead buffer, default is 12 * 300k = 3,6 MB (Apr 2011) */
97    if ((dsi->buffer = malloc(dsi->dsireadbuf * dsi->server_quantum)) == NULL) {
98        LOG(log_error, logtype_dsi, "dsi_init_buffer: OOM");
99        AFP_PANIC("OOM in dsi_init_buffer");
100    }
101    dsi->start = dsi->buffer;
102    dsi->eof = dsi->buffer;
103    dsi->end = dsi->buffer + (dsi->dsireadbuf * dsi->server_quantum);
104}
105
106/*!
107 * Free any allocated ressources of the master afpd DSI objects and close server socket
108 */
109void dsi_free(DSI *dsi)
110{
111    close(dsi->serversock);
112    dsi->serversock = -1;
113
114    free(dsi->commands);
115    dsi->commands = NULL;
116
117    free(dsi->buffer);
118    dsi->buffer = NULL;
119
120#ifdef USE_ZEROCONF
121    free(dsi->bonjourname);
122    dsi->bonjourname = NULL;
123#endif
124}
125
126static struct itimerval itimer;
127/* accept the socket and do a little sanity checking */
128static pid_t dsi_tcp_open(DSI *dsi)
129{
130    pid_t pid;
131    SOCKLEN_T len;
132
133    len = sizeof(dsi->client);
134    dsi->socket = accept(dsi->serversock, (struct sockaddr *) &dsi->client, &len);
135
136#ifdef TCPWRAP
137    {
138        struct request_info req;
139        request_init(&req, RQ_DAEMON, "afpd", RQ_FILE, dsi->socket, NULL);
140        fromhost(&req);
141        if (!hosts_access(&req)) {
142            LOG(deny_severity, logtype_dsi, "refused connect from %s", eval_client(&req));
143            close(dsi->socket);
144            errno = ECONNREFUSED;
145            dsi->socket = -1;
146        }
147    }
148#endif /* TCPWRAP */
149
150    if (dsi->socket < 0)
151        return -1;
152
153    getitimer(ITIMER_PROF, &itimer);
154    if (0 == (pid = fork()) ) { /* child */
155        static struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}};
156        struct sigaction newact, oldact;
157        uint8_t block[DSI_BLOCKSIZ];
158        size_t stored;
159
160        /* reset signals */
161        server_reset_signal();
162
163#ifndef DEBUGGING
164        /* install an alarm to deal with non-responsive connections */
165        newact.sa_handler = timeout_handler;
166        sigemptyset(&newact.sa_mask);
167        newact.sa_flags = 0;
168        sigemptyset(&oldact.sa_mask);
169        oldact.sa_flags = 0;
170        setitimer(ITIMER_PROF, &itimer, NULL);
171
172        if ((sigaction(SIGALRM, &newact, &oldact) < 0) ||
173            (setitimer(ITIMER_REAL, &timer, NULL) < 0)) {
174            LOG(log_error, logtype_dsi, "dsi_tcp_open: %s", strerror(errno));
175            exit(EXITERR_SYS);
176        }
177#endif
178
179        dsi_init_buffer(dsi);
180
181        /* read in commands. this is similar to dsi_receive except
182         * for the fact that we do some sanity checking to prevent
183         * delinquent connections from causing mischief. */
184
185        /* read in the first two bytes */
186        len = dsi_stream_read(dsi, block, 2);
187        if (!len ) {
188            /* connection already closed, don't log it (normal OSX 10.3 behaviour) */
189            exit(EXITERR_CLNT);
190        }
191        if (len < 2 || (block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) {
192            LOG(log_error, logtype_dsi, "dsi_tcp_open: invalid header");
193            exit(EXITERR_CLNT);
194        }
195
196        /* read in the rest of the header */
197        stored = 2;
198        while (stored < DSI_BLOCKSIZ) {
199            len = dsi_stream_read(dsi, block + stored, sizeof(block) - stored);
200            if (len > 0)
201                stored += len;
202            else {
203                LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno));
204                exit(EXITERR_CLNT);
205            }
206        }
207
208        dsi->header.dsi_flags = block[0];
209        dsi->header.dsi_command = block[1];
210        memcpy(&dsi->header.dsi_requestID, block + 2,
211               sizeof(dsi->header.dsi_requestID));
212        memcpy(&dsi->header.dsi_data.dsi_code, block + 4, sizeof(dsi->header.dsi_data.dsi_code));
213        memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
214        memcpy(&dsi->header.dsi_reserved, block + 12,
215               sizeof(dsi->header.dsi_reserved));
216        dsi->clientID = ntohs(dsi->header.dsi_requestID);
217
218        /* make sure we don't over-write our buffers. */
219        dsi->cmdlen = min(ntohl(dsi->header.dsi_len), dsi->server_quantum);
220
221        stored = 0;
222        while (stored < dsi->cmdlen) {
223            len = dsi_stream_read(dsi, dsi->commands + stored, dsi->cmdlen - stored);
224            if (len > 0)
225                stored += len;
226            else {
227                LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno));
228                exit(EXITERR_CLNT);
229            }
230        }
231
232        /* stop timer and restore signal handler */
233#ifndef DEBUGGING
234        memset(&timer, 0, sizeof(timer));
235        setitimer(ITIMER_REAL, &timer, NULL);
236        sigaction(SIGALRM, &oldact, NULL);
237#endif
238
239        LOG(log_info, logtype_dsi, "AFP/TCP session from %s:%u",
240            getip_string((struct sockaddr *)&dsi->client),
241            getip_port((struct sockaddr *)&dsi->client));
242    }
243
244    /* send back our pid */
245    return pid;
246}
247
248/* get it from the interface list */
249#ifndef IFF_SLAVE
250#define IFF_SLAVE 0
251#endif
252
253static void guess_interface(DSI *dsi, const char *hostname, const char *port)
254{
255    int fd;
256    char **start, **list;
257    struct ifreq ifr;
258    struct sockaddr_in *sa = (struct sockaddr_in *)&dsi->server;
259
260    start = list = getifacelist();
261    if (!start)
262        return;
263
264    fd = socket(PF_INET, SOCK_STREAM, 0);
265
266    while (list && *list) {
267        strlcpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
268        list++;
269
270
271        if (ioctl(dsi->serversock, SIOCGIFFLAGS, &ifr) < 0)
272            continue;
273
274        if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
275            continue;
276
277        if (!(ifr.ifr_flags & (IFF_UP | IFF_RUNNING)) )
278            continue;
279
280        if (ioctl(fd, SIOCGIFADDR, &ifr) < 0)
281            continue;
282
283        memset(&dsi->server, 0, sizeof(struct sockaddr_storage));
284        sa->sin_family = AF_INET;
285        sa->sin_port = htons(atoi(port));
286        sa->sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
287
288        LOG(log_info, logtype_dsi, "dsi_tcp: '%s:%s' on interface '%s' will be used instead.",
289            getip_string((struct sockaddr *)&dsi->server), port, ifr.ifr_name);
290        goto iflist_done;
291    }
292
293    LOG(log_note, logtype_dsi, "dsi_tcp: couldn't find network interface with IP address to advertice, "
294        "check to make sure \"%s\" is in /etc/hosts or can be resolved with DNS, or "
295        "add a netinterface that is not a loopback or point-2-point type", hostname);
296
297iflist_done:
298    close(fd);
299    freeifacelist(start);
300}
301
302
303#ifndef AI_NUMERICSERV
304#define AI_NUMERICSERV 0
305#endif
306
307/*!
308 * Initialize DSI over TCP
309 *
310 * @param dsi        (rw) DSI handle
311 * @param hostname   (r)  pointer to hostname string
312 * @param inaddress  (r)  Optional IPv4 or IPv6 address with an optional port, may be NULL
313 * @param inport     (r)  pointer to port string
314 *
315 * Creates listening AFP/DSI socket. If the parameter inaddress is NULL, then we listen
316 * on the wildcard address, ie on all interfaces. That should mean listening on the IPv6
317 * address "::" on IPv4/IPv6 dual stack kernels, accepting both v4 and v6 requests.
318 *
319 * If the parameter inaddress is not NULL, then we only listen on the given address.
320 * The parameter may contain a port number using the URL format for address and port:
321 *
322 *   IPv4, IPv4:port, IPv6, [IPv6], [IPv6]:port
323 *
324 * Parameter inport must be a valid pointer to a port string and is used if the inaddress
325 * parameter doesn't contain a port.
326 *
327 * @returns 0 on success, -1 on failure
328 */
329int dsi_tcp_init(DSI *dsi, const char *hostname, const char *inaddress, const char *inport)
330{
331    EC_INIT;
332    int                flag, err;
333    char              *address = NULL, *port = NULL;
334    struct addrinfo    hints, *servinfo, *p;
335
336    /* inaddress may be NULL */
337    AFP_ASSERT(dsi && hostname && inport);
338
339    if (inaddress)
340        /* Check whether address is of the from IP:PORT and split */
341        EC_ZERO( tokenize_ip_port(inaddress, &address, &port) );
342
343    if (port == NULL)
344        /* inport is supposed to always contain a valid port string */
345        EC_NULL( port = strdup(inport) );
346
347    /* Prepare hint for getaddrinfo */
348    memset(&hints, 0, sizeof hints);
349#if !defined(FREEBSD)
350    hints.ai_family = AF_UNSPEC;
351#endif
352    hints.ai_socktype = SOCK_STREAM;
353    hints.ai_flags = AI_NUMERICSERV;
354
355    if ( ! address) {
356        hints.ai_flags |= AI_PASSIVE;
357#if defined(FREEBSD)
358        hints.ai_family = AF_INET6;
359#endif
360    } else {
361        hints.ai_flags |= AI_NUMERICHOST;
362#if defined(FREEBSD)
363        hints.ai_family = AF_UNSPEC;
364#endif
365    }
366    if ((ret = getaddrinfo(address, port, &hints, &servinfo)) != 0) {
367        LOG(log_error, logtype_dsi, "dsi_tcp_init(%s): getaddrinfo: %s\n", address ? address : "*", gai_strerror(ret));
368        EC_FAIL;
369    }
370
371    /* loop through all the results and bind to the first we can */
372    for (p = servinfo; p != NULL; p = p->ai_next) {
373        if ((dsi->serversock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
374            LOG(log_info, logtype_dsi, "dsi_tcp_init: socket: %s", strerror(errno));
375            continue;
376        }
377
378        /*
379         * Set some socket options:
380         * SO_REUSEADDR deals w/ quick close/opens
381         * TCP_NODELAY diables Nagle
382         */
383#ifdef SO_REUSEADDR
384        flag = 1;
385        setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
386#endif
387#if defined(FREEBSD) && defined(IPV6_BINDV6ONLY)
388        int on = 0;
389        setsockopt(dsi->serversock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (on));
390#endif
391
392#ifndef SOL_TCP
393#define SOL_TCP IPPROTO_TCP
394#endif
395        flag = 1;
396        setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
397
398        if (bind(dsi->serversock, p->ai_addr, p->ai_addrlen) == -1) {
399            close(dsi->serversock);
400            LOG(log_info, logtype_dsi, "dsi_tcp_init: bind: %s\n", strerror(errno));
401            continue;
402        }
403
404        if (listen(dsi->serversock, DSI_TCPMAXPEND) < 0) {
405            close(dsi->serversock);
406            LOG(log_info, logtype_dsi, "dsi_tcp_init: listen: %s\n", strerror(errno));
407            continue;
408        }
409
410        break;
411    }
412
413    if (p == NULL)  {
414        LOG(log_error, logtype_dsi, "dsi_tcp_init: no suitable network config for TCP socket");
415        freeaddrinfo(servinfo);
416        EC_FAIL;
417    }
418
419    /* Copy struct sockaddr to struct sockaddr_storage */
420    memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
421    freeaddrinfo(servinfo);
422
423    /* Point protocol specific functions to tcp versions */
424    dsi->proto_open = dsi_tcp_open;
425    dsi->proto_close = dsi_tcp_close;
426
427    /* get real address for GetStatus. */
428
429    if (address) {
430        /* address is a parameter, use it 'as is' */
431        goto EC_CLEANUP;
432    }
433
434    /* Prepare hint for getaddrinfo */
435    memset(&hints, 0, sizeof hints);
436    hints.ai_family = AF_UNSPEC;
437    hints.ai_socktype = SOCK_STREAM;
438
439    if ((err = getaddrinfo(hostname, port, &hints, &servinfo)) != 0) {
440        LOG(log_info, logtype_dsi, "dsi_tcp_init: getaddrinfo '%s': %s\n", hostname, gai_strerror(err));
441        goto interfaces;
442    }
443
444    for (p = servinfo; p != NULL; p = p->ai_next) {
445        if (p->ai_family == AF_INET) { // IPv4
446            struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
447            if ( (ipv4->sin_addr.s_addr & htonl(0x7f000000)) != htonl(0x7f000000) )
448                break;
449        } else { // IPv6
450            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
451            unsigned char ipv6loopb[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
452            if ((memcmp(ipv6->sin6_addr.s6_addr, ipv6loopb, 16)) != 0)
453                break;
454        }
455    }
456
457    if (p) {
458        /* Store found address in dsi->server */
459        memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
460        freeaddrinfo(servinfo);
461        goto EC_CLEANUP;
462    }
463    LOG(log_info, logtype_dsi, "dsi_tcp: hostname '%s' resolves to loopback address", hostname);
464    freeaddrinfo(servinfo);
465
466interfaces:
467    guess_interface(dsi, hostname, port ? port : "548");
468
469EC_CLEANUP:
470    if (address)
471        free(address);
472    if (port)
473        free(port);
474    EC_EXIT;
475}
476
477