]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
Revert 1.10. This was pointless anyway.
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * $Id: afp_config.c,v 1.11 2001-12-15 18:34:39 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     /* 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         syslog( LOG_ERR, "main: atp_open: %s", strerror(errno) );
200         free(config);
201         return NULL;
202     }
203
204     if ((asp = asp_init( atp )) == NULL) {
205         syslog( LOG_ERR, "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         syslog( LOG_ERR, "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         syslog( LOG_ERR, "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     syslog( LOG_INFO, "%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         syslog( LOG_ERR, "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         syslog( LOG_ERR, "main: dsi_init: %s", strerror(errno) );
295         free(config);
296         return NULL;
297     }
298
299     if (options->flags & OPTION_PROXY) {
300         syslog(LOG_INFO, "ASIP proxy initialized for %s:%d (%s)",
301                inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
302                VERSION);
303     } else {
304         syslog(LOG_INFO, "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         syslog(LOG_ERR, "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 (dsi->server.sin_port == afpovertcp->s_port) {
322         snprintf(srvloc_url, sizeof(srvloc_url), "afp://%s/?NAME=%s", inet_ntoa(dsi->server.sin_addr), options->hostname);
323     }
324     else {
325         snprintf(srvloc_url, sizeof(srvloc_url), "afp://%s:%d/?NAME=%s", inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), options->hostname);
326     }
327
328     err = SLPReg(hslp,
329                  srvloc_url,
330                  SLP_LIFETIME_MAXIMUM,
331                  "",
332                  "",
333                  SLP_TRUE,
334                  SRVLOC_callback,
335                  &callbackerr);
336     if (err != SLP_OK) {
337         syslog(LOG_ERR, "Error registering %s with SRVLOC", srvloc_url);
338         goto srvloc_reg_err;
339     }
340
341     if (callbackerr != SLP_OK) {
342         syslog(LOG_ERR, "Error in callback trying to register %s with SRVLOC", srvloc_url);
343         goto srvloc_reg_err;
344     }
345
346     syslog(LOG_INFO, "Sucessfully registered %s with SRVLOC", srvloc_url);
347
348 srvloc_reg_err:
349     SLPClose(hslp);
350 #endif /* USE_SRVLOC */
351
352
353     config->fd = dsi->serversock;
354     config->obj.handle = dsi;
355     config->obj.config = config;
356     config->obj.proto = AFPPROTO_DSI;
357
358     memcpy(&config->obj.options, options, sizeof(struct afp_options));
359     /* get rid of any appletalk info. we use the fact that the DSI
360      * stuff is done after the ASP stuff. */
361     p = config->obj.options.server;
362     if (p && (q = strchr(p, ':')))
363         *q = '\0';
364
365     config->optcount = refcount;
366     (*refcount)++;
367
368     config->server_start = dsi_start;
369 #ifdef USE_SRVLOC
370     config->server_cleanup = dsi_cleanup;
371 #endif /* USE_SRVLOC */
372     return config;
373 }
374
375 /* allocate server configurations. this should really store the last
376  * entry in config->last or something like that. that would make
377  * supporting multiple dsi transports easier. */
378 static AFPConfig *AFPConfigInit(const struct afp_options *options,
379                                 const struct afp_options *defoptions)
380 {
381     AFPConfig *config = NULL, *next = NULL;
382     unsigned char *refcount;
383
384     if ((refcount = (unsigned char *)
385                     calloc(1, sizeof(unsigned char))) == NULL) {
386         syslog( LOG_ERR, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
387         return NULL;
388     }
389
390 #ifndef NO_DDP
391     /* handle asp transports */
392     if ((options->transports & AFPTRANS_DDP) &&
393             (config = ASPConfigInit(options, refcount)))
394         config->defoptions = defoptions;
395 #endif /* NO_DDP */
396
397     /* handle dsi transports and dsi proxies. we only proxy
398      * for DSI connections. */
399
400     /* this should have something like the following:
401      * for (i=mindsi; i < maxdsi; i++)
402      *   if (options->transports & (1 << i) && 
403      *     (next = DSIConfigInit(options, refcount, i)))
404      *     next->defoptions = defoptions;
405      */
406     if ((options->transports & AFPTRANS_TCP) &&
407             (((options->flags & OPTION_PROXY) == 0) ||
408              ((options->flags & OPTION_PROXY) && config))
409             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
410         next->defoptions = defoptions;
411
412     /* load in all the authentication modules. we can load the same
413        things multiple times if necessary. however, loading different
414        things with the same names will cause complaints. by not loading
415        in any uams with proxies, we prevent ddp connections from succeeding.
416     */
417     auth_load(options->uampath, options->uamlist);
418
419     /* this should be able to accept multiple dsi transports. i think
420      * the only thing that gets affected is the net addresses. */
421     status_init(config, next, options);
422
423     /* attach dsi config to tail of asp config */
424     if (config) {
425         config->next = next;
426         return config;
427     }
428
429     return next;
430 }
431
432 /* fill in the appropriate bits for each interface */
433 AFPConfig *configinit(struct afp_options *cmdline)
434 {
435     FILE *fp;
436     char buf[LINESIZE + 1], *p, have_option = 0;
437     struct afp_options options;
438     AFPConfig *config, *first = NULL;
439
440     /* if config file doesn't exist, load defaults */
441     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
442         return AFPConfigInit(cmdline, cmdline);
443
444     /* scan in the configuration file */
445     while (!feof(fp)) {
446         if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
447             continue;
448
449         /* a little pre-processing to get rid of spaces and end-of-lines */
450         p = buf;
451         while (p && isspace(*p))
452             p++;
453         if (!p || (*p == '\0'))
454             continue;
455
456         have_option = 1;
457
458         memcpy(&options, cmdline, sizeof(options));
459         if (!afp_options_parseline(p, &options))
460             continue;
461
462         /* this should really get a head and a tail to simplify things. */
463         if (!first) {
464             if ((first = AFPConfigInit(&options, cmdline)))
465                 config = first->next ? first->next : first;
466         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
467             config = config->next->next ? config->next->next : config->next;
468         }
469     }
470
471     fclose(fp);
472
473     if (!have_option)
474         return AFPConfigInit(cmdline, cmdline);
475
476     return first;
477 }