]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
c82532955ea0b36161521e4b4b21d518ce3d79e4
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * $Id: afp_config.c,v 1.22.6.5 2004-04-27 22:47:32 didg 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 #endif /* USE_SRVLOC */
52
53 #include "globals.h"
54 #include "afp_config.h"
55 #include "uam_auth.h"
56 #include "status.h"
57
58 #define LINESIZE 1024  
59
60 /* get rid of unneeded configurations. i use reference counts to deal
61  * w/ multiple configs sharing the same afp_options. oh, to dream of
62  * garbage collection ... */
63 void configfree(AFPConfig *configs, const AFPConfig *config)
64 {
65     AFPConfig *p, *q;
66
67     for (p = configs; p; p = q) {
68         q = p->next;
69         if (p == config)
70             continue;
71
72         /* do a little reference counting */
73         if (--(*p->optcount) < 1) {
74             afp_options_free(&p->obj.options, p->defoptions);
75             free(p->optcount);
76         }
77
78         switch (p->obj.proto) {
79 #ifndef NO_DDP
80         case AFPPROTO_ASP:
81             free(p->obj.Obj);
82             free(p->obj.Type);
83             free(p->obj.Zone);
84             atp_close(((ASP) p->obj.handle)->asp_atp);
85             free(p->obj.handle);
86             break;
87 #endif /* no afp/asp */
88         case AFPPROTO_DSI:
89             close(p->fd);
90             free(p->obj.handle);
91             break;
92         }
93         free(p);
94     }
95 }
96
97 #ifdef USE_SRVLOC
98 static void SRVLOC_callback(SLPHandle hslp, SLPError errcode, void *cookie) {
99     *(SLPError*)cookie = errcode;
100 }
101
102 static char hex[17] = "0123456789abcdef";
103
104 static char * srvloc_encode(const struct afp_options *options, const char *name)
105 {
106         static char buf[512];
107         char *conv_name;
108         unsigned char *p;
109         unsigned int i = 0;
110 #ifndef NO_DDP
111         char *Obj, *Type = "", *Zone = "";
112 #endif
113
114         /* Convert name to maccharset */
115         if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
116                          name, strlen(name), &conv_name)) )
117                 return (char*)name;
118
119         /* Escape characters */
120         p = conv_name;
121         while (*p && i<(sizeof(buf)-4)) {
122             if (*p == '@')
123                 break;
124             else if (isspace(*p)) {
125                 buf[i++] = '%';
126                 buf[i++] = '2';
127                 buf[i++] = '0';
128                 p++;
129             }   
130             else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
131                 buf[i++] = '%';
132                 buf[i++] = hex[*p >> 4];
133                 buf[i++] = hex[*p++ & 15];
134             }
135             else {
136                 buf[i++] = *p++;
137             }
138         }
139         buf[i] = '\0';
140
141 #ifndef NO_DDP
142         /* Add ZONE,  */
143         if (nbp_name(options->server, &Obj, &Type, &Zone )) {
144                 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
145         }
146         else {
147                 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
148         }
149 #endif
150         free (conv_name);
151
152         return buf;
153 }
154 #endif /* USE_SRVLOC */
155
156 #ifdef USE_SRVLOC
157 static void dsi_cleanup(const AFPConfig *config)
158 {
159     SLPError err;
160     SLPError callbackerr;
161     SLPHandle hslp;
162     DSI *dsi = (DSI *)config->obj.handle;
163
164     /*  Do nothing if we didn't register.  */
165     if (!dsi || dsi->srvloc_url[0] == '\0')
166         return;
167
168     err = SLPOpen("en", SLP_FALSE, &hslp);
169     if (err != SLP_OK) {
170         LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
171         goto srvloc_dereg_err;
172     }
173
174     err = SLPDereg(hslp,
175                    dsi->srvloc_url,
176                    SRVLOC_callback,
177                    &callbackerr);
178     if (err != SLP_OK) {
179         LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
180         goto srvloc_dereg_err;
181     }
182
183     if (callbackerr != SLP_OK) {
184         LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
185         goto srvloc_dereg_err;
186     }
187
188 srvloc_dereg_err:
189     dsi->srvloc_url[0] = '\0';
190     SLPClose(hslp);
191 }
192 #endif /* USE_SRVLOC */
193
194 #ifndef NO_DDP
195 static void asp_cleanup(const AFPConfig *config)
196 {
197     /* we need to stop tickle handler */
198     asp_stop_tickle();
199     nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
200                 &config->obj.options.ddpaddr);
201 }
202
203 /* these two are almost identical. it should be possible to collapse them
204  * into one with minimal junk. */
205 static int asp_start(AFPConfig *config, AFPConfig *configs,
206                      server_child *server_children)
207 {
208     ASP asp;
209
210     if (!(asp = asp_getsession(config->obj.handle, server_children,
211                                config->obj.options.tickleval))) {
212         LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
213         exit( 1 );
214     }
215
216     if (asp->child) {
217         configfree(configs, config); /* free a bunch of stuff */
218         afp_over_asp(&config->obj);
219         exit (0);
220     }
221
222     return 0;
223 }
224 #endif /* no afp/asp */
225
226 static int dsi_start(AFPConfig *config, AFPConfig *configs,
227                      server_child *server_children)
228 {
229     DSI *dsi;
230
231     if (!(dsi = dsi_getsession(config->obj.handle, server_children,
232                                config->obj.options.tickleval))) {
233         LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
234         exit( 1 );
235     }
236
237     /* we've forked. */
238     if (dsi->child) {
239         configfree(configs, config);
240         afp_over_dsi(&config->obj); /* start a session */
241         exit (0);
242     }
243
244     return 0;
245 }
246
247 #ifndef NO_DDP
248 static AFPConfig *ASPConfigInit(const struct afp_options *options,
249                                 unsigned char *refcount)
250 {
251     AFPConfig *config;
252     ATP atp;
253     ASP asp;
254     char *Obj, *Type = "AFPServer", *Zone = "*";
255
256     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
257         return NULL;
258
259     if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
260         LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
261         free(config);
262         return NULL;
263     }
264
265     if ((asp = asp_init( atp )) == NULL) {
266         LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
267         atp_close(atp);
268         free(config);
269         return NULL;
270     }
271
272     /* register asp server */
273     Obj = (char *) options->hostname;
274     if (nbp_name(options->server, &Obj, &Type, &Zone )) {
275         LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
276         goto serv_free_return;
277     }
278
279     /* dup Obj, Type and Zone as they get assigned to a single internal
280      * buffer by nbp_name */
281     if ((config->obj.Obj  = strdup(Obj)) == NULL)
282         goto serv_free_return;
283
284     if ((config->obj.Type = strdup(Type)) == NULL) {
285         free(config->obj.Obj);
286         goto serv_free_return;
287     }
288
289     if ((config->obj.Zone = strdup(Zone)) == NULL) {
290         free(config->obj.Obj);
291         free(config->obj.Type);
292         goto serv_free_return;
293     }
294
295     /* make sure we're not registered */
296     nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
297     if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
298         LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
299         free(config->obj.Obj);
300         free(config->obj.Type);
301         free(config->obj.Zone);
302         goto serv_free_return;
303     }
304
305     LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
306         ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
307         atp_sockaddr( atp )->sat_addr.s_node,
308         atp_sockaddr( atp )->sat_port, VERSION );
309
310     config->fd = atp_fileno(atp);
311     config->obj.handle = asp;
312     config->obj.config = config;
313     config->obj.proto = AFPPROTO_ASP;
314
315     memcpy(&config->obj.options, options, sizeof(struct afp_options));
316     config->optcount = refcount;
317     (*refcount)++;
318
319     config->server_start = asp_start;
320     config->server_cleanup = asp_cleanup;
321
322     return config;
323
324 serv_free_return:
325                     asp_close(asp);
326     free(config);
327     return NULL;
328 }
329 #endif /* no afp/asp */
330
331
332 static AFPConfig *DSIConfigInit(const struct afp_options *options,
333                                 unsigned char *refcount,
334                                 const dsi_proto protocol)
335 {
336     AFPConfig *config;
337     DSI *dsi;
338     char *p, *q;
339 #ifdef USE_SRVLOC
340     SLPError err;
341     SLPError callbackerr;
342     SLPHandle hslp;
343     struct servent *afpovertcp;
344     int afp_port = 548;
345     char *srvloc_hostname, *hostname;
346 #endif /* USE_SRVLOC */
347
348     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
349         LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
350         return NULL;
351     }
352
353     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
354                         options->ipaddr, options->port,
355                         options->flags & OPTION_PROXY,
356                         options->server_quantum)) == NULL) {
357         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
358         free(config);
359         return NULL;
360     }
361
362     if (options->flags & OPTION_PROXY) {
363         LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
364             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
365             VERSION);
366     } else {
367         LOG(log_info, logtype_afpd, "ASIP started on %s:%d(%d) (%s)",
368             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
369             dsi->serversock, VERSION);
370     }
371
372 #ifdef USE_SRVLOC
373     dsi->srvloc_url[0] = '\0';  /*  Mark that we haven't registered.  */
374     if (!(options->flags & OPTION_NOSLP)) {
375         err = SLPOpen("en", SLP_FALSE, &hslp);
376         if (err != SLP_OK) {
377             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
378             goto srvloc_reg_err;
379         }
380
381         /* XXX We don't want to tack on the port number if we don't have to.
382          * Why?
383          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
384          * use a non-default port, they can, but be aware, this server might
385          * not show up int the Network Browser.
386          */
387         afpovertcp = getservbyname("afpovertcp", "tcp");
388         if (afpovertcp != NULL) {
389             afp_port = afpovertcp->s_port;
390         }
391         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
392         p = NULL;
393         if (options->fqdn) {
394             hostname = options->fqdn;
395             p = strchr(hostname, ':');
396         }       
397         else 
398             hostname = inet_ntoa(dsi->server.sin_addr);
399         srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
400
401         if (strlen(srvloc_hostname) > (sizeof(dsi->srvloc_url) - strlen(hostname) - 21)) {
402             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
403             dsi->srvloc_url[0] = '\0';
404             goto srvloc_reg_err;
405         }
406         if ((p) || dsi->server.sin_port == afp_port) {
407             sprintf(dsi->srvloc_url, "afp://%s/?NAME=%s", hostname, srvloc_hostname);
408         }
409         else {
410             sprintf(dsi->srvloc_url, "afp://%s:%d/?NAME=%s", hostname, ntohs(dsi->server.sin_port), srvloc_hostname);
411         }
412
413         err = SLPReg(hslp,
414                      dsi->srvloc_url,
415                      SLP_LIFETIME_MAXIMUM,
416                      "",
417                      "",
418                      SLP_TRUE,
419                      SRVLOC_callback,
420                      &callbackerr);
421         if (err != SLP_OK) {
422             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
423             dsi->srvloc_url[0] = '\0';
424             goto srvloc_reg_err;
425         }
426
427         if (callbackerr != SLP_OK) {
428             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
429             dsi->srvloc_url[0] = '\0';
430             goto srvloc_reg_err;
431         }
432
433         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
434
435 srvloc_reg_err:
436         SLPClose(hslp);
437     }
438 #endif /* USE_SRVLOC */
439
440
441     config->fd = dsi->serversock;
442     config->obj.handle = dsi;
443     config->obj.config = config;
444     config->obj.proto = AFPPROTO_DSI;
445
446     memcpy(&config->obj.options, options, sizeof(struct afp_options));
447     /* get rid of any appletalk info. we use the fact that the DSI
448      * stuff is done after the ASP stuff. */
449     p = config->obj.options.server;
450     if (p && (q = strchr(p, ':')))
451         *q = '\0';
452
453     config->optcount = refcount;
454     (*refcount)++;
455
456     config->server_start = dsi_start;
457 #ifdef USE_SRVLOC
458     config->server_cleanup = dsi_cleanup;
459 #endif 
460     return config;
461 }
462
463 /* allocate server configurations. this should really store the last
464  * entry in config->last or something like that. that would make
465  * supporting multiple dsi transports easier. */
466 static AFPConfig *AFPConfigInit(const struct afp_options *options,
467                                 const struct afp_options *defoptions)
468 {
469     AFPConfig *config = NULL, *next = NULL;
470     unsigned char *refcount;
471
472     if ((refcount = (unsigned char *)
473                     calloc(1, sizeof(unsigned char))) == NULL) {
474         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
475         return NULL;
476     }
477
478 #ifndef NO_DDP
479     /* handle asp transports */
480     if ((options->transports & AFPTRANS_DDP) &&
481             (config = ASPConfigInit(options, refcount)))
482         config->defoptions = defoptions;
483 #endif /* NO_DDP */
484
485     /* handle dsi transports and dsi proxies. we only proxy
486      * for DSI connections. */
487
488     /* this should have something like the following:
489      * for (i=mindsi; i < maxdsi; i++)
490      *   if (options->transports & (1 << i) && 
491      *     (next = DSIConfigInit(options, refcount, i)))
492      *     next->defoptions = defoptions;
493      */
494     if ((options->transports & AFPTRANS_TCP) &&
495             (((options->flags & OPTION_PROXY) == 0) ||
496              ((options->flags & OPTION_PROXY) && config))
497             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
498         next->defoptions = defoptions;
499
500     /* load in all the authentication modules. we can load the same
501        things multiple times if necessary. however, loading different
502        things with the same names will cause complaints. by not loading
503        in any uams with proxies, we prevent ddp connections from succeeding.
504     */
505     auth_load(options->uampath, options->uamlist);
506
507     /* this should be able to accept multiple dsi transports. i think
508      * the only thing that gets affected is the net addresses. */
509     status_init(config, next, options);
510
511     /* attach dsi config to tail of asp config */
512     if (config) {
513         config->next = next;
514         return config;
515     }
516
517     return next;
518 }
519
520 /* fill in the appropriate bits for each interface */
521 AFPConfig *configinit(struct afp_options *cmdline)
522 {
523     FILE *fp;
524     char buf[LINESIZE + 1], *p, have_option = 0;
525     struct afp_options options;
526     AFPConfig *config, *first = NULL;
527
528     status_reset();
529     /* if config file doesn't exist, load defaults */
530     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
531     {
532         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
533             cmdline->configfile);
534         return AFPConfigInit(cmdline, cmdline);
535     }
536
537     LOG(log_debug, logtype_afpd, "Loading ConfigFile"); 
538
539     /* scan in the configuration file */
540     while (!feof(fp)) {
541         if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
542             continue;
543
544         /* a little pre-processing to get rid of spaces and end-of-lines */
545         p = buf;
546         while (p && isspace(*p))
547             p++;
548         if (!p || (*p == '\0'))
549             continue;
550
551         have_option = 1;
552
553         memcpy(&options, cmdline, sizeof(options));
554         if (!afp_options_parseline(p, &options))
555             continue;
556
557         /* this should really get a head and a tail to simplify things. */
558         if (!first) {
559             if ((first = AFPConfigInit(&options, cmdline)))
560                 config = first->next ? first->next : first;
561         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
562             config = config->next->next ? config->next->next : config->next;
563         }
564     }
565
566     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
567     fclose(fp);
568
569     if (!have_option)
570         return AFPConfigInit(cmdline, cmdline);
571
572     return first;
573 }