]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
added some LOG messages
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * $Id: afp_config.c,v 1.19 2002-04-02 02:41:40 sibaz 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 <atalk/logger.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         LOG(log_error, logtype_afpd, "dsi_cleanup: 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         LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", srvloc_url);
122         goto srvloc_dereg_err;
123     }
124
125     if (callbackerr != SLP_OK) {
126         LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", 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         LOG(log_error, logtype_afpd, "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         LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
173         exit( 1 );
174     }
175
176     /* we've forked. */
177     if (dsi->child) {
178         configfree(configs, config);
179         afp_over_dsi(&config->obj); /* start a session */
180         exit (0);
181     }
182
183     return 0;
184 }
185
186 #ifndef NO_DDP
187 static AFPConfig *ASPConfigInit(const struct afp_options *options,
188                                 unsigned char *refcount)
189 {
190     AFPConfig *config;
191     ATP atp;
192     ASP asp;
193     char *Obj, *Type = "AFPServer", *Zone = "*";
194
195     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
196         return NULL;
197
198     if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
199         LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
200         free(config);
201         return NULL;
202     }
203
204     if ((asp = asp_init( atp )) == NULL) {
205         LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
206         atp_close(atp);
207         free(config);
208         return NULL;
209     }
210
211     /* register asp server */
212     Obj = (char *) options->hostname;
213     if (nbp_name(options->server, &Obj, &Type, &Zone )) {
214         LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
215         goto serv_free_return;
216     }
217
218     /* dup Obj, Type and Zone as they get assigned to a single internal
219      * buffer by nbp_name */
220     if ((config->obj.Obj  = strdup(Obj)) == NULL)
221         goto serv_free_return;
222
223     if ((config->obj.Type = strdup(Type)) == NULL) {
224         free(config->obj.Obj);
225         goto serv_free_return;
226     }
227
228     if ((config->obj.Zone = strdup(Zone)) == NULL) {
229         free(config->obj.Obj);
230         free(config->obj.Type);
231         goto serv_free_return;
232     }
233
234     /* make sure we're not registered */
235     nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
236     if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
237         LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
238         free(config->obj.Obj);
239         free(config->obj.Type);
240         free(config->obj.Zone);
241         goto serv_free_return;
242     }
243
244     LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
245         ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
246         atp_sockaddr( atp )->sat_addr.s_node,
247         atp_sockaddr( atp )->sat_port, VERSION );
248
249     config->fd = atp_fileno(atp);
250     config->obj.handle = asp;
251     config->obj.config = config;
252     config->obj.proto = AFPPROTO_ASP;
253
254     memcpy(&config->obj.options, options, sizeof(struct afp_options));
255     config->optcount = refcount;
256     (*refcount)++;
257
258     config->server_start = asp_start;
259     config->server_cleanup = asp_cleanup;
260
261     return config;
262
263 serv_free_return:
264                     asp_close(asp);
265     free(config);
266     return NULL;
267 }
268 #endif /* no afp/asp */
269
270
271 static AFPConfig *DSIConfigInit(const struct afp_options *options,
272                                 unsigned char *refcount,
273                                 const dsi_proto protocol)
274 {
275     AFPConfig *config;
276     DSI *dsi;
277     char *p, *q;
278 #ifdef USE_SRVLOC
279     SLPError err;
280     SLPError callbackerr;
281     SLPHandle hslp;
282     struct servent *afpovertcp;
283     int afp_port = 548;
284         char *srvloc_hostname;
285 #endif /* USE_SRVLOC */
286
287     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
288         LOG(log_error, logtype_afpd, "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         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
297         free(config);
298         return NULL;
299     }
300
301     if (options->flags & OPTION_PROXY) {
302         LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
303             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
304             VERSION);
305     } else {
306         LOG(log_info, logtype_afpd, "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         LOG(log_error, logtype_afpd, "DSIConfigInit: 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         srvloc_hostname = (options->server ? options->server : options->hostname);
324     if (afpovertcp != NULL) {
325         afp_port = afpovertcp->s_port;
326     }
327     if (strlen(srvloc_hostname) > (sizeof(srvloc_url) - strlen(inet_ntoa(dsi->server.sin_addr)) - 21)) {
328         LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
329         goto srvloc_reg_err;
330     }
331     if (dsi->server.sin_port == afp_port) {
332         sprintf(srvloc_url, "afp://%s/?NAME=%s", inet_ntoa(dsi->server.sin_addr), srvloc_hostname);
333     }
334     else {
335         sprintf(srvloc_url, "afp://%s:%d/?NAME=%s", inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), srvloc_hostname);
336     }
337
338     err = SLPReg(hslp,
339                  srvloc_url,
340                  SLP_LIFETIME_MAXIMUM,
341                  "",
342                  "",
343                  SLP_TRUE,
344                  SRVLOC_callback,
345                  &callbackerr);
346     if (err != SLP_OK) {
347         LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", srvloc_url);
348         goto srvloc_reg_err;
349     }
350
351     if (callbackerr != SLP_OK) {
352         LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", srvloc_url);
353         goto srvloc_reg_err;
354     }
355
356     LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", srvloc_url);
357
358 srvloc_reg_err:
359     SLPClose(hslp);
360 #endif /* USE_SRVLOC */
361
362
363     config->fd = dsi->serversock;
364     config->obj.handle = dsi;
365     config->obj.config = config;
366     config->obj.proto = AFPPROTO_DSI;
367
368     memcpy(&config->obj.options, options, sizeof(struct afp_options));
369     /* get rid of any appletalk info. we use the fact that the DSI
370      * stuff is done after the ASP stuff. */
371     p = config->obj.options.server;
372     if (p && (q = strchr(p, ':')))
373         *q = '\0';
374
375     config->optcount = refcount;
376     (*refcount)++;
377
378     config->server_start = dsi_start;
379 #ifdef USE_SRVLOC
380     config->server_cleanup = dsi_cleanup;
381 #endif /* USE_SRVLOC */
382     return config;
383 }
384
385 /* allocate server configurations. this should really store the last
386  * entry in config->last or something like that. that would make
387  * supporting multiple dsi transports easier. */
388 static AFPConfig *AFPConfigInit(const struct afp_options *options,
389                                 const struct afp_options *defoptions)
390 {
391     AFPConfig *config = NULL, *next = NULL;
392     unsigned char *refcount;
393
394     if ((refcount = (unsigned char *)
395                     calloc(1, sizeof(unsigned char))) == NULL) {
396         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
397         return NULL;
398     }
399
400 #ifndef NO_DDP
401     /* handle asp transports */
402     if ((options->transports & AFPTRANS_DDP) &&
403             (config = ASPConfigInit(options, refcount)))
404         config->defoptions = defoptions;
405 #endif /* NO_DDP */
406
407     /* handle dsi transports and dsi proxies. we only proxy
408      * for DSI connections. */
409
410     /* this should have something like the following:
411      * for (i=mindsi; i < maxdsi; i++)
412      *   if (options->transports & (1 << i) && 
413      *     (next = DSIConfigInit(options, refcount, i)))
414      *     next->defoptions = defoptions;
415      */
416     if ((options->transports & AFPTRANS_TCP) &&
417             (((options->flags & OPTION_PROXY) == 0) ||
418              ((options->flags & OPTION_PROXY) && config))
419             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
420         next->defoptions = defoptions;
421
422     /* load in all the authentication modules. we can load the same
423        things multiple times if necessary. however, loading different
424        things with the same names will cause complaints. by not loading
425        in any uams with proxies, we prevent ddp connections from succeeding.
426     */
427     auth_load(options->uampath, options->uamlist);
428
429     /* this should be able to accept multiple dsi transports. i think
430      * the only thing that gets affected is the net addresses. */
431     status_init(config, next, options);
432
433     /* attach dsi config to tail of asp config */
434     if (config) {
435         config->next = next;
436         return config;
437     }
438
439     return next;
440 }
441
442 /* fill in the appropriate bits for each interface */
443 AFPConfig *configinit(struct afp_options *cmdline)
444 {
445     FILE *fp;
446     char buf[LINESIZE + 1], *p, have_option = 0;
447     struct afp_options options;
448     AFPConfig *config, *first = NULL;
449
450     /* if config file doesn't exist, load defaults */
451     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
452     {
453         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
454             cmdline->configfile);
455         return AFPConfigInit(cmdline, cmdline);
456     }
457
458     LOG(log_debug, logtype_afpd, "Loading ConfigFile"); 
459
460     /* scan in the configuration file */
461     while (!feof(fp)) {
462         if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
463             continue;
464
465         /* a little pre-processing to get rid of spaces and end-of-lines */
466         p = buf;
467         while (p && isspace(*p))
468             p++;
469         if (!p || (*p == '\0'))
470             continue;
471
472         have_option = 1;
473
474         memcpy(&options, cmdline, sizeof(options));
475         if (!afp_options_parseline(p, &options))
476             continue;
477
478         /* this should really get a head and a tail to simplify things. */
479         if (!first) {
480             if ((first = AFPConfigInit(&options, cmdline)))
481                 config = first->next ? first->next : first;
482         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
483             config = config->next->next ? config->next->next : config->next;
484         }
485     }
486
487     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
488     fclose(fp);
489
490     if (!have_option)
491         return AFPConfigInit(cmdline, cmdline);
492
493     return first;
494 }