]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
merged logging code into main branch. use configure option --without-logfile to...
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * $Id: afp_config.c,v 1.14 2002-01-04 04:45:47 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_default, "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_default, "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_default, "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_default, "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_default, "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_default, "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_default, "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_default, "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_default, "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_default, "%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 #endif /* USE_SRVLOC */
284
285     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
286         LOG(log_error, logtype_default, "DSIConfigInit: malloc(config): %s", strerror(errno) );
287         return NULL;
288     }
289
290     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
291                         options->ipaddr, options->port,
292                         options->flags & OPTION_PROXY,
293                         options->server_quantum)) == NULL) {
294         LOG(log_error, logtype_default, "main: dsi_init: %s", strerror(errno) );
295         free(config);
296         return NULL;
297     }
298
299     if (options->flags & OPTION_PROXY) {
300         LOG(log_info, logtype_default, "ASIP proxy initialized for %s:%d (%s)",
301                inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
302                VERSION);
303     } else {
304         LOG(log_info, logtype_default, "ASIP started on %s:%d(%d) (%s)",
305                inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
306                dsi->serversock, VERSION);
307     }
308
309 #ifdef USE_SRVLOC
310     err = SLPOpen("en", SLP_FALSE, &hslp);
311     if (err != SLP_OK) {
312         LOG(log_error, logtype_default, "DSIConfigInit: Error opening SRVLOC handle");
313         goto srvloc_reg_err;
314     }
315
316     /* XXX We don't want to tack on the port number if we don't have to.  Why?
317      * Well, this seems to break MacOS < 10.  If the user _really_ wants to
318      * use a non-default port, they can, but be aware, this server might not
319      * show up int the Network Browser. */
320     afpovertcp = getservbyname("afpovertcp", "tcp");
321     if (strlen(options->hostname) > (sizeof(srvloc_url) - strlen(inet_ntoa(dsi->server.sin_addr)) - 21)) {
322         LOG(log_error, logtype_default, "DSIConfigInit: Hostname is too long for SRVLOC");
323         goto srvloc_reg_err;
324     }
325     if (dsi->server.sin_port == afpovertcp->s_port) {
326         sprintf(srvloc_url, "afp://%s/?NAME=%s", inet_ntoa(dsi->server.sin_addr), options->hostname);
327     }
328     else {
329         sprintf(srvloc_url, "afp://%s:%d/?NAME=%s", inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), options->hostname);
330     }
331
332     err = SLPReg(hslp,
333                  srvloc_url,
334                  SLP_LIFETIME_MAXIMUM,
335                  "",
336                  "",
337                  SLP_TRUE,
338                  SRVLOC_callback,
339                  &callbackerr);
340     if (err != SLP_OK) {
341         LOG(log_error, logtype_default, "DSIConfigInit: Error registering %s with SRVLOC", srvloc_url);
342         goto srvloc_reg_err;
343     }
344
345     if (callbackerr != SLP_OK) {
346         LOG(log_error, logtype_default, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", srvloc_url);
347         goto srvloc_reg_err;
348     }
349
350     LOG(log_info, logtype_default, "Sucessfully registered %s with SRVLOC", srvloc_url);
351
352 srvloc_reg_err:
353     SLPClose(hslp);
354 #endif /* USE_SRVLOC */
355
356
357     config->fd = dsi->serversock;
358     config->obj.handle = dsi;
359     config->obj.config = config;
360     config->obj.proto = AFPPROTO_DSI;
361
362     memcpy(&config->obj.options, options, sizeof(struct afp_options));
363     /* get rid of any appletalk info. we use the fact that the DSI
364      * stuff is done after the ASP stuff. */
365     p = config->obj.options.server;
366     if (p && (q = strchr(p, ':')))
367         *q = '\0';
368
369     config->optcount = refcount;
370     (*refcount)++;
371
372     config->server_start = dsi_start;
373 #ifdef USE_SRVLOC
374     config->server_cleanup = dsi_cleanup;
375 #endif /* USE_SRVLOC */
376     return config;
377 }
378
379 /* allocate server configurations. this should really store the last
380  * entry in config->last or something like that. that would make
381  * supporting multiple dsi transports easier. */
382 static AFPConfig *AFPConfigInit(const struct afp_options *options,
383                                 const struct afp_options *defoptions)
384 {
385     AFPConfig *config = NULL, *next = NULL;
386     unsigned char *refcount;
387
388     if ((refcount = (unsigned char *)
389                     calloc(1, sizeof(unsigned char))) == NULL) {
390         LOG(log_error, logtype_default, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
391         return NULL;
392     }
393
394 #ifndef NO_DDP
395     /* handle asp transports */
396     if ((options->transports & AFPTRANS_DDP) &&
397             (config = ASPConfigInit(options, refcount)))
398         config->defoptions = defoptions;
399 #endif /* NO_DDP */
400
401     /* handle dsi transports and dsi proxies. we only proxy
402      * for DSI connections. */
403
404     /* this should have something like the following:
405      * for (i=mindsi; i < maxdsi; i++)
406      *   if (options->transports & (1 << i) && 
407      *     (next = DSIConfigInit(options, refcount, i)))
408      *     next->defoptions = defoptions;
409      */
410     if ((options->transports & AFPTRANS_TCP) &&
411             (((options->flags & OPTION_PROXY) == 0) ||
412              ((options->flags & OPTION_PROXY) && config))
413             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
414         next->defoptions = defoptions;
415
416     /* load in all the authentication modules. we can load the same
417        things multiple times if necessary. however, loading different
418        things with the same names will cause complaints. by not loading
419        in any uams with proxies, we prevent ddp connections from succeeding.
420     */
421     auth_load(options->uampath, options->uamlist);
422
423     /* this should be able to accept multiple dsi transports. i think
424      * the only thing that gets affected is the net addresses. */
425     status_init(config, next, options);
426
427     /* attach dsi config to tail of asp config */
428     if (config) {
429         config->next = next;
430         return config;
431     }
432
433     return next;
434 }
435
436 /* fill in the appropriate bits for each interface */
437 AFPConfig *configinit(struct afp_options *cmdline)
438 {
439     FILE *fp;
440     char buf[LINESIZE + 1], *p, have_option = 0;
441     struct afp_options options;
442     AFPConfig *config, *first = NULL;
443
444     /* if config file doesn't exist, load defaults */
445     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
446         return AFPConfigInit(cmdline, cmdline);
447
448     /* scan in the configuration file */
449     while (!feof(fp)) {
450         if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
451             continue;
452
453         /* a little pre-processing to get rid of spaces and end-of-lines */
454         p = buf;
455         while (p && isspace(*p))
456             p++;
457         if (!p || (*p == '\0'))
458             continue;
459
460         have_option = 1;
461
462         memcpy(&options, cmdline, sizeof(options));
463         if (!afp_options_parseline(p, &options))
464             continue;
465
466         /* this should really get a head and a tail to simplify things. */
467         if (!first) {
468             if ((first = AFPConfigInit(&options, cmdline)))
469                 config = first->next ? first->next : first;
470         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
471             config = config->next->next ? config->next->next : config->next;
472         }
473     }
474
475     fclose(fp);
476
477     if (!have_option)
478         return AFPConfigInit(cmdline, cmdline);
479
480     return first;
481 }