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