]> arthur.barton.de Git - netatalk.git/blob - libatalk/dsi/dsi_tcp.c
39c7b6aeef622d6f7d8ef5f738deec81bc0e5a55
[netatalk.git] / libatalk / dsi / dsi_tcp.c
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 #define USE_TCP_NODELAY
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <netdb.h>
17 #include <sys/types.h>
18 #include <sys/time.h>
19 #include <sys/socket.h>
20
21 #include <sys/ioctl.h>
22 #include <net/if.h>
23 #include <netinet/tcp.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26
27 #include <signal.h>
28 #include <syslog.h>
29
30 #ifdef __svr4__
31 #include <sys/sockio.h>
32 #endif
33
34 #ifdef TCPWRAP
35 #include <tcpd.h>
36 int allow_severity = LOG_INFO;
37 int deny_severity = LOG_WARNING;
38 #endif
39
40 #include <atalk/dsi.h>
41 #include <atalk/compat.h>
42 #include <atalk/util.h>
43 #include <netatalk/endian.h>
44 #include "dsi_private.h"
45
46 #define min(a,b)  ((a) < (b) ? (a) : (b))
47
48 #ifndef DSI_TCPMAXPEND
49 #define DSI_TCPMAXPEND      20       /* max # of pending connections */
50 #endif
51
52 #ifndef DSI_TCPTIMEOUT
53 #define DSI_TCPTIMEOUT      120     /* timeout in seconds for connections */
54 #endif
55
56
57 /* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
58 #ifndef SOCKLEN_T
59 #define SOCKLEN_T unsigned int
60 #endif
61
62 static void dsi_tcp_close(DSI *dsi)
63 {
64   if (dsi->socket == -1)
65     return;
66
67   close(dsi->socket);
68   dsi->socket = -1;
69 }
70
71 /* alarm handler for tcp_open */
72 static void timeout_handler()
73 {
74   syslog(LOG_ERR, "dsi_tcp_open: connection timed out");
75   exit(1);
76 }
77
78 /* accept the socket and do a little sanity checking */
79 static int dsi_tcp_open(DSI *dsi)
80 {
81   pid_t pid;
82   SOCKLEN_T len;
83
84   len = sizeof(dsi->client);
85   dsi->socket = accept(dsi->serversock, (struct sockaddr *) &dsi->client,
86                        &len);
87
88 #ifdef TCPWRAP
89   {
90     struct request_info req;
91     request_init(&req, RQ_DAEMON, dsi->program, RQ_FILE, dsi->socket, NULL);
92     fromhost(&req);
93     if (!hosts_access(&req)) {
94       syslog(deny_severity, "refused connect from %s", eval_client(&req));
95       close(dsi->socket);
96       errno = ECONNREFUSED;
97       dsi->socket = -1;
98     }
99   }
100 #endif
101
102   if (dsi->socket < 0)
103     return -1;
104
105   if ((pid = fork()) == 0) { /* child */
106     static const struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}};
107     struct sigaction newact, oldact;
108     u_int8_t block[DSI_BLOCKSIZ];
109     size_t stored;
110     
111     /* reset a couple signals */
112     signal(SIGTERM, SIG_DFL); 
113     signal(SIGHUP, SIG_DFL);
114
115     /* install an alarm to deal with non-responsive connections */
116     memset(&newact, 0, sizeof(newact));
117     newact.sa_handler = timeout_handler;
118     if ((sigaction(SIGALRM, &newact, &oldact) < 0) ||
119         (setitimer(ITIMER_REAL, &timer, NULL) < 0)) {
120         syslog(LOG_ERR, "dsi_tcp_open: %m");
121         exit(1);
122     }
123     
124     /* read in commands. this is similar to dsi_receive except
125      * for the fact that we do some sanity checking to prevent
126      * delinquent connections from causing mischief. */
127     
128     /* read in the first two bytes */
129     dsi_stream_read(dsi, block, 2);
130     if ((block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) {
131       syslog(LOG_ERR, "dsi_tcp_open: invalid header");
132       exit(1);
133     }      
134     
135     /* read in the rest of the header */
136     stored = 2;
137     while (stored < DSI_BLOCKSIZ) {
138       len = dsi_stream_read(dsi, block + stored, sizeof(block) - stored);
139       if (len > 0)
140         stored += len;
141       else {
142         syslog(LOG_ERR, "dsi_tcp_open: stream_read: %m");
143         exit(1);
144       }
145     }
146     
147     dsi->header.dsi_flags = block[0];
148     dsi->header.dsi_command = block[1];
149     memcpy(&dsi->header.dsi_requestID, block + 2, 
150            sizeof(dsi->header.dsi_requestID));
151     memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
152     memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
153     memcpy(&dsi->header.dsi_reserved, block + 12,
154            sizeof(dsi->header.dsi_reserved));
155     dsi->clientID = ntohs(dsi->header.dsi_requestID);
156     
157     /* make sure we don't over-write our buffers. */
158     dsi->cmdlen = min(ntohl(dsi->header.dsi_len), DSI_CMDSIZ);
159     
160     stored = 0;
161     while (stored < dsi->cmdlen) {
162       len = dsi_stream_read(dsi, dsi->commands + stored, dsi->cmdlen - stored);
163       if (len > 0)
164         stored += len;
165       else {
166         syslog(LOG_ERR, "dsi_tcp_open: stream_read: %m");
167         exit(1);
168       }
169     }
170     
171     /* restore signal */
172     sigaction(SIGALRM, &oldact, NULL);
173
174     syslog(LOG_INFO,"ASIP session:%u(%d) from %s:%u(%d)", 
175            ntohs(dsi->server.sin_port), dsi->serversock, 
176            inet_ntoa(dsi->client.sin_addr), ntohs(dsi->client.sin_port),
177            dsi->socket);
178   }
179   
180   /* send back our pid */
181   return pid;
182 }
183
184 /* this needs to accept passed in addresses */
185 int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
186                  const u_int16_t ipport, const int proxy)
187 {
188   struct servent     *service;
189   struct hostent     *host;
190   int                port;
191
192   dsi->protocol = DSI_TCPIP;
193
194   /* create a socket */
195   if (proxy)
196     dsi->serversock = -1;
197   else if ((dsi->serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
198     return 0;
199       
200   /* find port */
201   if (ipport)
202     port = htons(ipport);
203   else if ((service = getservbyname("afpovertcp", "tcp")))
204     port = service->s_port;
205   else
206     port = htons(DSI_AFPOVERTCP_PORT);
207
208   /* find address */
209   if (!address) 
210     dsi->server.sin_addr.s_addr = htonl(INADDR_ANY);
211   else if (inet_aton(address, &dsi->server.sin_addr) == 0) {
212     syslog(LOG_INFO, "dsi_tcp: invalid address (%s)", address);
213     return 0;
214   }
215
216   dsi->server.sin_family = AF_INET;
217   dsi->server.sin_port = port;
218
219   if (!proxy) {
220     /* this deals w/ quick close/opens */    
221 #ifdef SO_REUSEADDR
222     port = 1;
223     setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &port, sizeof(port));
224 #endif
225
226 #ifdef USE_TCP_NODELAY 
227 #ifndef SOL_TCP
228 #define SOL_TCP IPPROTO_TCP
229 #endif
230     port = 1;
231     setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &port, sizeof(port));
232 #endif
233
234     /* now, bind the socket and set it up for listening */
235     if ((bind(dsi->serversock, (struct sockaddr *) &dsi->server, 
236               sizeof(dsi->server)) < 0) || 
237         (listen(dsi->serversock, DSI_TCPMAXPEND) < 0)) {
238       close(dsi->serversock);
239       return 0;
240     }
241   }
242
243   /* get real address for GetStatus. we'll go through the list of 
244    * interfaces if necessary. */
245   if (!address) {
246     if ((host = gethostbyname(hostname))) /* we can resolve the name */
247       dsi->server.sin_addr.s_addr = ((struct in_addr *) host->h_addr)->s_addr;
248     else {
249       char **start, **list;
250       struct ifreq ifr;
251
252       /* get it from the interface list */
253       start = list = getifacelist();
254       while (list && *list) {
255         strncpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
256         list++;
257
258 #ifndef IFF_SLAVE
259 #define IFF_SLAVE 0
260 #endif
261         if (ioctl(dsi->serversock, SIOCGIFFLAGS, &ifr) < 0)
262           continue;
263
264         if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
265           continue;
266
267         if ((ifr.ifr_flags & IFF_UP) == 0)
268           continue;
269
270         if (ioctl(dsi->serversock, SIOCGIFADDR, &ifr) < 0)
271           continue;
272         
273         dsi->server.sin_addr.s_addr = 
274           ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
275         syslog(LOG_INFO, "dsi_tcp: Can't resolve hostname (%s).\n"
276                "%s on interface %s will be used instead.", hostname,
277                inet_ntoa(dsi->server.sin_addr), ifr.ifr_name);
278         goto iflist_done;
279
280       }
281       syslog(LOG_INFO, "dsi_tcp (Chooser will not select afp/tcp)\n\
282 Check to make sure %s is in /etc/hosts and the correct domain is in\n\
283 /etc/resolv.conf: %m", hostname);
284
285 iflist_done:
286       if (start)
287         freeifacelist(start);
288     }
289   }
290
291   /* everything's set up. now point protocol specific functions to 
292    * tcp versions */
293   dsi->proto_open = dsi_tcp_open;
294   dsi->proto_close = dsi_tcp_close;
295   return 1;
296
297 }
298