]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
Add a new -timeout option to afpd. This option specifies how many tickles
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * $Id: afp_config.c,v 1.10 2001-12-15 06:25:44 jmarcus Exp $
3  *
4  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16
17 /* STDC check */
18 #if STDC_HEADERS
19 #include <string.h>
20 #else /* STDC_HEADERS */
21 #ifndef HAVE_STRCHR
22 #define strchr index
23 #define strrchr index
24 #endif /* HAVE_STRCHR */
25 char *strchr (), *strrchr ();
26 #ifndef HAVE_MEMCPY
27 #define memcpy(d,s,n) bcopy ((s), (d), (n))
28 #define memmove(d,s,n) bcopy ((s), (d), (n))
29 #endif /* ! HAVE_MEMCPY */
30 #endif /* STDC_HEADERS */
31
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif /* HAVE_UNISTD_H */
35 #include <ctype.h>
36 #include <syslog.h>
37
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41
42 #include <atalk/dsi.h>
43 #include <atalk/atp.h>
44 #include <atalk/asp.h>
45 #include <atalk/nbp.h>
46 #include <atalk/afp.h>
47 #include <atalk/compat.h>
48 #include <atalk/server_child.h>
49 #ifdef USE_SRVLOC
50 #include <slp.h>
51 static char srvloc_url[512];
52 #endif /* USE_SRVLOC */
53
54 #include "globals.h"
55 #include "afp_config.h"
56 #include "uam_auth.h"
57 #include "status.h"
58
59 #define LINESIZE 1024  
60
61 /* get rid of unneeded configurations. i use reference counts to deal
62  * w/ multiple configs sharing the same afp_options. oh, to dream of
63  * garbage collection ... */
64 void configfree(AFPConfig *configs, const AFPConfig *config)
65 {
66     AFPConfig *p, *q;
67
68     for (p = configs; p; p = q) {
69         q = p->next;
70         if (p == config)
71             continue;
72
73         /* do a little reference counting */
74         if (--(*p->optcount) < 1) {
75             afp_options_free(&p->obj.options, p->defoptions);
76             free(p->optcount);
77         }
78
79         switch (p->obj.proto) {
80 #ifndef NO_DDP
81         case AFPPROTO_ASP:
82             free(p->obj.Obj);
83             free(p->obj.Type);
84             free(p->obj.Zone);
85             atp_close(((ASP) p->obj.handle)->asp_atp);
86             free(p->obj.handle);
87             break;
88 #endif /* no afp/asp */
89         case AFPPROTO_DSI:
90             close(p->fd);
91             free(p->obj.handle);
92             break;
93         }
94         free(p);
95     }
96 }
97
98 #ifdef USE_SRVLOC
99 static void SRVLOC_callback(SLPHandle hslp, SLPError errcode, void *cookie) {
100     *(SLPError*)cookie = errcode;
101 }
102 #endif /* USE_SRVLOC */
103
104 #ifdef USE_SRVLOC
105 static void dsi_cleanup(const AFPConfig *config)
106 {
107     SLPError err;
108     SLPError callbackerr;
109     SLPHandle hslp;
110     err = SLPOpen("en", SLP_FALSE, &hslp);
111     if (err != SLP_OK) {
112         syslog(LOG_ERR, "Error opening SRVLOC handle");
113         goto srvloc_dereg_err;
114     }
115
116     err = SLPDereg(hslp,
117                    srvloc_url,
118                    SRVLOC_callback,
119                    &callbackerr);
120     if (err != SLP_OK) {
121         syslog(LOG_ERR, "Error unregistering %s from SRVLOC", srvloc_url);
122         goto srvloc_dereg_err;
123     }
124
125     if (callbackerr != SLP_OK) {
126         syslog(LOG_ERR, "Error in callback while trying to unregister %s from SRVLOC (%i)", srvloc_url, callbackerr);
127         goto srvloc_dereg_err;
128     }
129
130 srvloc_dereg_err:
131     SLPClose(hslp);
132 }
133 #endif /* USE_SRVLOC */
134
135 #ifndef NO_DDP
136 static void asp_cleanup(const AFPConfig *config)
137 {
138     nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
139                 &config->obj.options.ddpaddr);
140 }
141
142 /* these two are almost identical. it should be possible to collapse them
143  * into one with minimal junk. */
144 static int asp_start(AFPConfig *config, AFPConfig *configs,
145                      server_child *server_children)
146 {
147     ASP asp;
148
149     if (!(asp = asp_getsession(config->obj.handle, server_children,
150                                config->obj.options.tickleval))) {
151         syslog( LOG_ERR, "main: asp_getsession: %s", strerror(errno) );
152         exit( 1 );
153     }
154
155     if (asp->child) {
156         configfree(configs, config); /* free a bunch of stuff */
157         afp_over_asp(&config->obj);
158         exit (0);
159     }
160
161     return 0;
162 }
163 #endif /* no afp/asp */
164
165 static int dsi_start(AFPConfig *config, AFPConfig *configs,
166                      server_child *server_children)
167 {
168     DSI *dsi;
169
170     if (!(dsi = dsi_getsession(config->obj.handle, server_children,
171                                config->obj.options.tickleval))) {
172         syslog( LOG_ERR, "main: dsi_getsession: %s", strerror(errno) );
173         exit( 1 );
174     }
175
176     config->obj.handle = dsi;
177
178     /* we've forked. */
179     if (dsi->child) {
180         configfree(configs, config);
181         afp_over_dsi(&config->obj); /* start a session */
182         exit (0);
183     }
184
185     return 0;
186 }
187
188 #ifndef NO_DDP
189 static AFPConfig *ASPConfigInit(const struct afp_options *options,
190                                 unsigned char *refcount)
191 {
192     AFPConfig *config;
193     ATP atp;
194     ASP asp;
195     char *Obj, *Type = "AFPServer", *Zone = "*";
196
197     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
198         return NULL;
199
200     if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
201         syslog( LOG_ERR, "main: atp_open: %s", strerror(errno) );
202         free(config);
203         return NULL;
204     }
205
206     if ((asp = asp_init( atp )) == NULL) {
207         syslog( LOG_ERR, "main: asp_init: %s", strerror(errno) );
208         atp_close(atp);
209         free(config);
210         return NULL;
211     }
212
213     /* register asp server */
214     Obj = (char *) options->hostname;
215     if (nbp_name(options->server, &Obj, &Type, &Zone )) {
216         syslog( LOG_ERR, "main: can't parse %s", options->server );
217         goto serv_free_return;
218     }
219
220     /* dup Obj, Type and Zone as they get assigned to a single internal
221      * buffer by nbp_name */
222     if ((config->obj.Obj  = strdup(Obj)) == NULL)
223         goto serv_free_return;
224
225     if ((config->obj.Type = strdup(Type)) == NULL) {
226         free(config->obj.Obj);
227         goto serv_free_return;
228     }
229
230     if ((config->obj.Zone = strdup(Zone)) == NULL) {
231         free(config->obj.Obj);
232         free(config->obj.Type);
233         goto serv_free_return;
234     }
235
236     /* make sure we're not registered */
237     nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
238     if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
239         syslog( LOG_ERR, "Can't register %s:%s@%s", Obj, Type, Zone );
240         free(config->obj.Obj);
241         free(config->obj.Type);
242         free(config->obj.Zone);
243         goto serv_free_return;
244     }
245
246     syslog( LOG_INFO, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
247             ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
248             atp_sockaddr( atp )->sat_addr.s_node,
249             atp_sockaddr( atp )->sat_port, VERSION );
250
251     config->fd = atp_fileno(atp);
252     config->obj.handle = asp;
253     config->obj.config = config;
254     config->obj.proto = AFPPROTO_ASP;
255
256     memcpy(&config->obj.options, options, sizeof(struct afp_options));
257     config->optcount = refcount;
258     (*refcount)++;
259
260     config->server_start = asp_start;
261     config->server_cleanup = asp_cleanup;
262
263     return config;
264
265 serv_free_return:
266                     asp_close(asp);
267     free(config);
268     return NULL;
269 }
270 #endif /* no afp/asp */
271
272
273 static AFPConfig *DSIConfigInit(const struct afp_options *options,
274                                 unsigned char *refcount,
275                                 const dsi_proto protocol)
276 {
277     AFPConfig *config;
278     DSI *dsi;
279     char *p, *q;
280 #ifdef USE_SRVLOC
281     SLPError err;
282     SLPError callbackerr;
283     SLPHandle hslp;
284     struct servent *afpovertcp;
285 #endif /* USE_SRVLOC */
286
287     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
288         syslog( LOG_ERR, "DSIConfigInit: malloc(config): %s", strerror(errno) );
289         return NULL;
290     }
291
292     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
293                         options->ipaddr, options->port,
294                         options->flags & OPTION_PROXY,
295                         options->server_quantum)) == NULL) {
296         syslog( LOG_ERR, "main: dsi_init: %s", strerror(errno) );
297         free(config);
298         return NULL;
299     }
300
301     if (options->flags & OPTION_PROXY) {
302         syslog(LOG_INFO, "ASIP proxy initialized for %s:%d (%s)",
303                inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
304                VERSION);
305     } else {
306         syslog(LOG_INFO, "ASIP started on %s:%d(%d) (%s)",
307                inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
308                dsi->serversock, VERSION);
309     }
310
311 #ifdef USE_SRVLOC
312     err = SLPOpen("en", SLP_FALSE, &hslp);
313     if (err != SLP_OK) {
314         syslog(LOG_ERR, "Error opening SRVLOC handle");
315         goto srvloc_reg_err;
316     }
317
318     /* XXX We don't want to tack on the port number if we don't have to.  Why?
319      * Well, this seems to break MacOS < 10.  If the user _really_ wants to
320      * use a non-default port, they can, but be aware, this server might not
321      * show up int the Network Browser. */
322     afpovertcp = getservbyname("afpovertcp", "tcp");
323     if (dsi->server.sin_port == afpovertcp->s_port) {
324         snprintf(srvloc_url, sizeof(srvloc_url), "afp://%s/?NAME=%s", inet_ntoa(dsi->server.sin_addr), options->hostname);
325     }
326     else {
327         snprintf(srvloc_url, sizeof(srvloc_url), "afp://%s:%d/?NAME=%s", inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), options->hostname);
328     }
329
330     err = SLPReg(hslp,
331                  srvloc_url,
332                  SLP_LIFETIME_MAXIMUM,
333                  "",
334                  "",
335                  SLP_TRUE,
336                  SRVLOC_callback,
337                  &callbackerr);
338     if (err != SLP_OK) {
339         syslog(LOG_ERR, "Error registering %s with SRVLOC", srvloc_url);
340         goto srvloc_reg_err;
341     }
342
343     if (callbackerr != SLP_OK) {
344         syslog(LOG_ERR, "Error in callback trying to register %s with SRVLOC", srvloc_url);
345         goto srvloc_reg_err;
346     }
347
348     syslog(LOG_INFO, "Sucessfully registered %s with SRVLOC", srvloc_url);
349
350 srvloc_reg_err:
351     SLPClose(hslp);
352 #endif /* USE_SRVLOC */
353
354
355     config->fd = dsi->serversock;
356     config->obj.handle = dsi;
357     config->obj.config = config;
358     config->obj.proto = AFPPROTO_DSI;
359
360     memcpy(&config->obj.options, options, sizeof(struct afp_options));
361     /* get rid of any appletalk info. we use the fact that the DSI
362      * stuff is done after the ASP stuff. */
363     p = config->obj.options.server;
364     if (p && (q = strchr(p, ':')))
365         *q = '\0';
366
367     config->optcount = refcount;
368     (*refcount)++;
369
370     config->server_start = dsi_start;
371 #ifdef USE_SRVLOC
372     config->server_cleanup = dsi_cleanup;
373 #endif /* USE_SRVLOC */
374     return config;
375 }
376
377 /* allocate server configurations. this should really store the last
378  * entry in config->last or something like that. that would make
379  * supporting multiple dsi transports easier. */
380 static AFPConfig *AFPConfigInit(const struct afp_options *options,
381                                 const struct afp_options *defoptions)
382 {
383     AFPConfig *config = NULL, *next = NULL;
384     unsigned char *refcount;
385
386     if ((refcount = (unsigned char *)
387                     calloc(1, sizeof(unsigned char))) == NULL) {
388         syslog( LOG_ERR, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
389         return NULL;
390     }
391
392 #ifndef NO_DDP
393     /* handle asp transports */
394     if ((options->transports & AFPTRANS_DDP) &&
395             (config = ASPConfigInit(options, refcount)))
396         config->defoptions = defoptions;
397 #endif /* NO_DDP */
398
399     /* handle dsi transports and dsi proxies. we only proxy
400      * for DSI connections. */
401
402     /* this should have something like the following:
403      * for (i=mindsi; i < maxdsi; i++)
404      *   if (options->transports & (1 << i) && 
405      *     (next = DSIConfigInit(options, refcount, i)))
406      *     next->defoptions = defoptions;
407      */
408     if ((options->transports & AFPTRANS_TCP) &&
409             (((options->flags & OPTION_PROXY) == 0) ||
410              ((options->flags & OPTION_PROXY) && config))
411             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
412         next->defoptions = defoptions;
413
414     /* load in all the authentication modules. we can load the same
415        things multiple times if necessary. however, loading different
416        things with the same names will cause complaints. by not loading
417        in any uams with proxies, we prevent ddp connections from succeeding.
418     */
419     auth_load(options->uampath, options->uamlist);
420
421     /* this should be able to accept multiple dsi transports. i think
422      * the only thing that gets affected is the net addresses. */
423     status_init(config, next, options);
424
425     /* attach dsi config to tail of asp config */
426     if (config) {
427         config->next = next;
428         return config;
429     }
430
431     return next;
432 }
433
434 /* fill in the appropriate bits for each interface */
435 AFPConfig *configinit(struct afp_options *cmdline)
436 {
437     FILE *fp;
438     char buf[LINESIZE + 1], *p, have_option = 0;
439     struct afp_options options;
440     AFPConfig *config, *first = NULL;
441
442     /* if config file doesn't exist, load defaults */
443     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
444         return AFPConfigInit(cmdline, cmdline);
445
446     /* scan in the configuration file */
447     while (!feof(fp)) {
448         if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
449             continue;
450
451         /* a little pre-processing to get rid of spaces and end-of-lines */
452         p = buf;
453         while (p && isspace(*p))
454             p++;
455         if (!p || (*p == '\0'))
456             continue;
457
458         have_option = 1;
459
460         memcpy(&options, cmdline, sizeof(options));
461         if (!afp_options_parseline(p, &options))
462             continue;
463
464         /* this should really get a head and a tail to simplify things. */
465         if (!first) {
466             if ((first = AFPConfigInit(&options, cmdline)))
467                 config = first->next ? first->next : first;
468         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
469             config = config->next->next ? config->next->next : config->next;
470         }
471     }
472
473     fclose(fp);
474
475     if (!have_option)
476         return AFPConfigInit(cmdline, cmdline);
477
478     return first;
479 }