• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.5/libatalk/dsi/
1/*
2 * $Id: dsi_tcp.c,v 1.25 2009-12-08 22:34:37 didg Exp $
3 *
4 * Copyright (c) 1997, 1998 Adrian Sun (asun@zoology.washington.edu)
5 * All rights reserved. See COPYRIGHT.
6 *
7 * this provides both proto_open() and proto_close() to account for
8 * protocol specific initialization and shutdown procedures. all the
9 * read/write stuff is done in dsi_stream.c.  */
10
11#ifdef HAVE_CONFIG_H
12#include "config.h"
13#endif /* HAVE_CONFIG_H */
14
15#define USE_TCP_NODELAY
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#ifdef HAVE_UNISTD_H
20#include <unistd.h>
21#endif /* HAVE_UNISTD_H */
22#include <errno.h>
23#ifdef HAVE_NETDB_H
24#include <netdb.h>
25#endif /* HAVE_NETDB_H */
26#include <sys/types.h>
27#include <sys/time.h>
28#include <sys/socket.h>
29
30#ifdef HAVE_STDINT_H
31#include <stdint.h>
32#endif /* HAVE_STDINT_H */
33
34#include <sys/ioctl.h>
35#ifdef TRU64
36#include <sys/mbuf.h>
37#include <net/route.h>
38#endif /* TRU64 */
39#include <net/if.h>
40#include <netinet/tcp.h>
41#include <netinet/in.h>
42#include <arpa/inet.h>
43
44#include <signal.h>
45#include <atalk/logger.h>
46
47#ifdef __svr4__
48#include <sys/sockio.h>
49#endif /* __svr4__ */
50
51#ifdef TCPWRAP
52#include <tcpd.h>
53int allow_severity = log_info;
54int deny_severity = log_warning;
55#endif /* TCPWRAP */
56
57#include <atalk/dsi.h>
58#include <atalk/compat.h>
59#include <atalk/util.h>
60#include <netatalk/endian.h>
61#include "dsi_private.h"
62
63#define min(a,b)  ((a) < (b) ? (a) : (b))
64
65#ifndef DSI_TCPMAXPEND
66#define DSI_TCPMAXPEND      20       /* max # of pending connections */
67#endif /* DSI_TCPMAXPEND */
68
69#ifndef DSI_TCPTIMEOUT
70#define DSI_TCPTIMEOUT      120     /* timeout in seconds for connections */
71#endif /* ! DSI_TCPTIMEOUT */
72
73
74/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
75#ifndef SOCKLEN_T
76#define SOCKLEN_T unsigned int
77#endif /* ! SOCKLEN_T */
78
79static void dsi_tcp_close(DSI *dsi)
80{
81    if (dsi->socket == -1)
82        return;
83
84    close(dsi->socket);
85    dsi->socket = -1;
86}
87
88/* alarm handler for tcp_open */
89static void timeout_handler(int sig _U_)
90{
91    LOG(log_error, logtype_dsi, "dsi_tcp_open: connection timed out");
92    exit(EXITERR_CLNT);
93}
94
95static struct itimerval itimer;
96/* accept the socket and do a little sanity checking */
97static int dsi_tcp_open(DSI *dsi)
98{
99    pid_t pid;
100    SOCKLEN_T len;
101
102    len = sizeof(dsi->client);
103    dsi->socket = accept(dsi->serversock, (struct sockaddr *) &dsi->client, &len);
104
105#ifdef TCPWRAP
106    {
107        struct request_info req;
108        request_init(&req, RQ_DAEMON, dsi->program, RQ_FILE, dsi->socket, NULL);
109        fromhost(&req);
110        if (!hosts_access(&req)) {
111            LOG(deny_severity, logtype_dsi, "refused connect from %s", eval_client(&req));
112            close(dsi->socket);
113            errno = ECONNREFUSED;
114            dsi->socket = -1;
115        }
116    }
117#endif /* TCPWRAP */
118
119    if (dsi->socket < 0)
120        return -1;
121
122    getitimer(ITIMER_PROF, &itimer);
123    if (0 == (pid = fork()) ) { /* child */
124        static struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}};
125        struct sigaction newact, oldact;
126        u_int8_t block[DSI_BLOCKSIZ];
127        size_t stored;
128
129        /* Immediateyl mark globally that we're a child now */
130        parent_or_child = 1;
131
132        /* reset signals */
133        server_reset_signal();
134
135#ifndef DEBUGGING
136        /* install an alarm to deal with non-responsive connections */
137        newact.sa_handler = timeout_handler;
138        sigemptyset(&newact.sa_mask);
139        newact.sa_flags = 0;
140        sigemptyset(&oldact.sa_mask);
141        oldact.sa_flags = 0;
142        setitimer(ITIMER_PROF, &itimer, NULL);
143
144        if ((sigaction(SIGALRM, &newact, &oldact) < 0) ||
145            (setitimer(ITIMER_REAL, &timer, NULL) < 0)) {
146            LOG(log_error, logtype_dsi, "dsi_tcp_open: %s", strerror(errno));
147            exit(EXITERR_SYS);
148        }
149#endif
150
151        /* read in commands. this is similar to dsi_receive except
152         * for the fact that we do some sanity checking to prevent
153         * delinquent connections from causing mischief. */
154
155        /* read in the first two bytes */
156        len = dsi_stream_read(dsi, block, 2);
157        if (!len ) {
158            /* connection already closed, don't log it (normal OSX 10.3 behaviour) */
159            exit(EXITERR_CLNT);
160        }
161        if (len < 2 || (block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) {
162            LOG(log_error, logtype_dsi, "dsi_tcp_open: invalid header");
163            exit(EXITERR_CLNT);
164        }
165
166        /* read in the rest of the header */
167        stored = 2;
168        while (stored < DSI_BLOCKSIZ) {
169            len = dsi_stream_read(dsi, block + stored, sizeof(block) - stored);
170            if (len > 0)
171                stored += len;
172            else {
173                LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno));
174                exit(EXITERR_CLNT);
175            }
176        }
177
178        dsi->header.dsi_flags = block[0];
179        dsi->header.dsi_command = block[1];
180        memcpy(&dsi->header.dsi_requestID, block + 2,
181               sizeof(dsi->header.dsi_requestID));
182        memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
183        memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
184        memcpy(&dsi->header.dsi_reserved, block + 12,
185               sizeof(dsi->header.dsi_reserved));
186        dsi->clientID = ntohs(dsi->header.dsi_requestID);
187
188        /* make sure we don't over-write our buffers. */
189        dsi->cmdlen = min(ntohl(dsi->header.dsi_len), DSI_CMDSIZ);
190
191        stored = 0;
192        while (stored < dsi->cmdlen) {
193            len = dsi_stream_read(dsi, dsi->commands + stored, dsi->cmdlen - stored);
194            if (len > 0)
195                stored += len;
196            else {
197                LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno));
198                exit(EXITERR_CLNT);
199            }
200        }
201
202        /* stop timer and restore signal handler */
203#ifndef DEBUGGING
204        memset(&timer, 0, sizeof(timer));
205        setitimer(ITIMER_REAL, &timer, NULL);
206        sigaction(SIGALRM, &oldact, NULL);
207#endif
208
209        LOG(log_info, logtype_dsi, "AFP/TCP session from %s:%u",
210            getip_string((struct sockaddr *)&dsi->client),
211            getip_port((struct sockaddr *)&dsi->client));
212    }
213
214    /* send back our pid */
215    return pid;
216}
217
218/* get it from the interface list */
219#ifndef IFF_SLAVE
220#define IFF_SLAVE 0
221#endif
222/* foxconn modified start */
223static void guess_interface(DSI *dsi, const char *hostname, const char *port)
224{
225    int fd;
226    char **start, **list;
227    struct ifreq ifr;
228    struct sockaddr_in *sa = (struct sockaddr_in *)&dsi->server;
229
230    fd = socket(PF_INET, SOCK_STREAM, 0);
231
232    strcpy(ifr.ifr_name, "br0");
233
234    if (ioctl(dsi->serversock, SIOCGIFFLAGS, &ifr) >= 0)
235    {
236        if ( !(ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE)) )
237        {
238            if ( ifr.ifr_flags & (IFF_UP | IFF_RUNNING) )
239            {
240                if (ioctl(fd, SIOCGIFADDR, &ifr) >= 0)
241                {
242                    memset(&dsi->server, 0, sizeof(struct sockaddr_storage));
243                    sa->sin_family = AF_INET;
244                    sa->sin_port = htons(atoi(port));
245                    sa->sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
246
247                    LOG(log_info, logtype_dsi, "dsi_tcp: '%s:%s' on interface '%s' will be used instead.",
248                        getip_string((struct sockaddr *)&dsi->server), port, ifr.ifr_name);
249                }
250            }
251        }
252    }
253
254    LOG(log_info, logtype_dsi, "dsi_tcp (Chooser will not select afp/tcp) "
255        "Check to make sure %s is in /etc/hosts and the correct domain is in "
256        "/etc/resolv.conf: %s", hostname, strerror(errno));
257
258iflist_done:
259    close(fd);
260}
261/* foxconn modified end */
262
263#ifndef AI_NUMERICSERV
264#define AI_NUMERICSERV 0
265#endif
266
267/* this needs to accept passed in addresses */
268int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
269                 const char *port, const int proxy)
270{
271    int                ret;
272    int                flag;
273    struct addrinfo    hints, *servinfo, *p;
274
275    dsi->protocol = DSI_TCPIP;
276
277    /* Prepare hint for getaddrinfo */
278    memset(&hints, 0, sizeof hints);
279#if !defined(FREEBSD)
280    hints.ai_family = AF_UNSPEC;
281#endif
282    hints.ai_socktype = SOCK_STREAM;
283    hints.ai_flags = AI_NUMERICSERV;
284
285    if ( ! address) {
286        //hints.ai_flags |= AI_PASSIVE;
287        hints.ai_flags = AI_PASSIVE;    /* foxconn modified */
288#if defined(FREEBSD)
289        hints.ai_family = AF_INET6;
290#endif
291    } else {
292        hints.ai_flags |= AI_NUMERICHOST;
293#if defined(FREEBSD)
294        hints.ai_family = AF_UNSPEC;
295#endif
296    }
297    if ((ret = getaddrinfo(address ? address : NULL, port ? port : "548", &hints, &servinfo)) != 0) {
298        LOG(log_error, logtype_dsi, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret));
299        return 0;
300    }
301
302    /* create a socket */
303    if (proxy)
304        dsi->serversock = -1;
305    else {
306        /* loop through all the results and bind to the first we can */
307        for (p = servinfo; p != NULL; p = p->ai_next) {
308            if ((dsi->serversock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
309                LOG(log_info, logtype_dsi, "dsi_tcp_init: socket: %s", strerror(errno));
310                continue;
311            }
312
313            /*
314             * Set some socket options:
315             * SO_REUSEADDR deals w/ quick close/opens
316             * TCP_NODELAY diables Nagle
317             */
318#ifdef SO_REUSEADDR
319            flag = 1;
320            setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
321#endif
322#if defined(FREEBSD) && defined(IPV6_BINDV6ONLY)
323            int on = 0;
324            setsockopt(dsi->serversock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (on));
325#endif
326
327#ifdef USE_TCP_NODELAY
328#ifndef SOL_TCP
329#define SOL_TCP IPPROTO_TCP
330#endif
331            flag = 1;
332            setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
333#endif /* USE_TCP_NODELAY */
334
335            if (bind(dsi->serversock, p->ai_addr, p->ai_addrlen) == -1) {
336                close(dsi->serversock);
337                LOG(log_info, logtype_dsi, "dsi_tcp_init: bind: %s\n", strerror(errno));
338                continue;
339            }
340
341            if (listen(dsi->serversock, DSI_TCPMAXPEND) < 0) {
342                close(dsi->serversock);
343                LOG(log_info, logtype_dsi, "dsi_tcp_init: listen: %s\n", strerror(errno));
344                continue;
345            }
346
347            break;
348        }
349
350        if (p == NULL)  {
351            LOG(log_error, logtype_dsi, "dsi_tcp_init: no suitable network config for TCP socket");
352            freeaddrinfo(servinfo);
353            return 0;
354        }
355
356        /* Copy struct sockaddr to struct sockaddr_storage */
357        memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
358        freeaddrinfo(servinfo);
359    } /* if (proxy) */
360
361    /* Point protocol specific functions to tcp versions */
362    dsi->proto_open = dsi_tcp_open;
363    dsi->proto_close = dsi_tcp_close;
364
365    /* get real address for GetStatus. */
366
367    if (address) {
368        /* address is a parameter, use it 'as is' */
369        return 1;
370    }
371
372    /* Prepare hint for getaddrinfo */
373    memset(&hints, 0, sizeof hints);
374    hints.ai_family = AF_UNSPEC;
375    hints.ai_socktype = SOCK_STREAM;
376
377    goto interfaces; /* foxconn added */
378    if ((ret = getaddrinfo(hostname, port ? port : "548", &hints, &servinfo)) != 0) {
379        LOG(log_info, logtype_dsi, "dsi_tcp_init: getaddrinfo '%s': %s\n", hostname, gai_strerror(ret));
380        goto interfaces;
381    }
382
383    for (p = servinfo; p != NULL; p = p->ai_next) {
384        if (p->ai_family == AF_INET) { // IPv4
385            struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
386            if ( (ipv4->sin_addr.s_addr & htonl(0x7f000000)) != htonl(0x7f000000) )
387                break;
388        } else { // IPv6
389            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
390            unsigned char ipv6loopb[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
391            if ((memcmp(ipv6->sin6_addr.s6_addr, ipv6loopb, 16)) != 0)
392                break;
393        }
394    }
395
396    if (p) {
397        /* Store found address in dsi->server */
398        memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
399        freeaddrinfo(servinfo);
400        return 1;
401    }
402    LOG(log_info, logtype_dsi, "dsi_tcp: hostname '%s' resolves to loopback address", hostname);
403    freeaddrinfo(servinfo);
404
405interfaces:
406    guess_interface(dsi, hostname, port ? port : "548");
407    return 1;
408}
409
410