]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
2a2911c374910684b4342eff5b45e1208a13de37
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * $Id: afp_config.c,v 1.22.6.3 2004-01-23 14:00:15 bfernhomberg 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     nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
198                 &config->obj.options.ddpaddr);
199 }
200
201 /* these two are almost identical. it should be possible to collapse them
202  * into one with minimal junk. */
203 static int asp_start(AFPConfig *config, AFPConfig *configs,
204                      server_child *server_children)
205 {
206     ASP asp;
207
208     if (!(asp = asp_getsession(config->obj.handle, server_children,
209                                config->obj.options.tickleval))) {
210         LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
211         exit( 1 );
212     }
213
214     if (asp->child) {
215         configfree(configs, config); /* free a bunch of stuff */
216         afp_over_asp(&config->obj);
217         exit (0);
218     }
219
220     return 0;
221 }
222 #endif /* no afp/asp */
223
224 static int dsi_start(AFPConfig *config, AFPConfig *configs,
225                      server_child *server_children)
226 {
227     DSI *dsi;
228
229     if (!(dsi = dsi_getsession(config->obj.handle, server_children,
230                                config->obj.options.tickleval))) {
231         LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
232         exit( 1 );
233     }
234
235     /* we've forked. */
236     if (dsi->child) {
237         configfree(configs, config);
238         afp_over_dsi(&config->obj); /* start a session */
239         exit (0);
240     }
241
242     return 0;
243 }
244
245 #ifndef NO_DDP
246 static AFPConfig *ASPConfigInit(const struct afp_options *options,
247                                 unsigned char *refcount)
248 {
249     AFPConfig *config;
250     ATP atp;
251     ASP asp;
252     char *Obj, *Type = "AFPServer", *Zone = "*";
253
254     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
255         return NULL;
256
257     if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
258         LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
259         free(config);
260         return NULL;
261     }
262
263     if ((asp = asp_init( atp )) == NULL) {
264         LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
265         atp_close(atp);
266         free(config);
267         return NULL;
268     }
269
270     /* register asp server */
271     Obj = (char *) options->hostname;
272     if (nbp_name(options->server, &Obj, &Type, &Zone )) {
273         LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
274         goto serv_free_return;
275     }
276
277     /* dup Obj, Type and Zone as they get assigned to a single internal
278      * buffer by nbp_name */
279     if ((config->obj.Obj  = strdup(Obj)) == NULL)
280         goto serv_free_return;
281
282     if ((config->obj.Type = strdup(Type)) == NULL) {
283         free(config->obj.Obj);
284         goto serv_free_return;
285     }
286
287     if ((config->obj.Zone = strdup(Zone)) == NULL) {
288         free(config->obj.Obj);
289         free(config->obj.Type);
290         goto serv_free_return;
291     }
292
293     /* make sure we're not registered */
294     nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
295     if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
296         LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
297         free(config->obj.Obj);
298         free(config->obj.Type);
299         free(config->obj.Zone);
300         goto serv_free_return;
301     }
302
303     LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
304         ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
305         atp_sockaddr( atp )->sat_addr.s_node,
306         atp_sockaddr( atp )->sat_port, VERSION );
307
308     config->fd = atp_fileno(atp);
309     config->obj.handle = asp;
310     config->obj.config = config;
311     config->obj.proto = AFPPROTO_ASP;
312
313     memcpy(&config->obj.options, options, sizeof(struct afp_options));
314     config->optcount = refcount;
315     (*refcount)++;
316
317     config->server_start = asp_start;
318     config->server_cleanup = asp_cleanup;
319
320     return config;
321
322 serv_free_return:
323                     asp_close(asp);
324     free(config);
325     return NULL;
326 }
327 #endif /* no afp/asp */
328
329
330 static AFPConfig *DSIConfigInit(const struct afp_options *options,
331                                 unsigned char *refcount,
332                                 const dsi_proto protocol)
333 {
334     AFPConfig *config;
335     DSI *dsi;
336     char *p, *q;
337 #ifdef USE_SRVLOC
338     SLPError err;
339     SLPError callbackerr;
340     SLPHandle hslp;
341     struct servent *afpovertcp;
342     int afp_port = 548;
343     char *srvloc_hostname, *hostname;
344 #endif /* USE_SRVLOC */
345
346     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
347         LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
348         return NULL;
349     }
350
351     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
352                         options->ipaddr, options->port,
353                         options->flags & OPTION_PROXY,
354                         options->server_quantum)) == NULL) {
355         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
356         free(config);
357         return NULL;
358     }
359
360     if (options->flags & OPTION_PROXY) {
361         LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
362             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
363             VERSION);
364     } else {
365         LOG(log_info, logtype_afpd, "ASIP started on %s:%d(%d) (%s)",
366             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
367             dsi->serversock, VERSION);
368     }
369
370 #ifdef USE_SRVLOC
371     dsi->srvloc_url[0] = '\0';  /*  Mark that we haven't registered.  */
372     if (!(options->flags & OPTION_NOSLP)) {
373         err = SLPOpen("en", SLP_FALSE, &hslp);
374         if (err != SLP_OK) {
375             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
376             goto srvloc_reg_err;
377         }
378
379         /* XXX We don't want to tack on the port number if we don't have to.
380          * Why?
381          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
382          * use a non-default port, they can, but be aware, this server might
383          * not show up int the Network Browser.
384          */
385         afpovertcp = getservbyname("afpovertcp", "tcp");
386         if (afpovertcp != NULL) {
387             afp_port = afpovertcp->s_port;
388         }
389         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
390         p = NULL;
391         if (options->fqdn) {
392             hostname = strdup(options->fqdn);
393             p = strchr(hostname, ':');
394         }       
395         else 
396             hostname = inet_ntoa(dsi->server.sin_addr);
397         srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
398
399         if (strlen(srvloc_hostname) > (sizeof(dsi->srvloc_url) - strlen(hostname) - 21)) {
400             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
401             dsi->srvloc_url[0] = '\0';
402             goto srvloc_reg_err;
403         }
404         if ((p) || dsi->server.sin_port == afp_port) {
405             sprintf(dsi->srvloc_url, "afp://%s/?NAME=%s", hostname, srvloc_hostname);
406         }
407         else {
408             sprintf(dsi->srvloc_url, "afp://%s:%d/?NAME=%s", hostname, ntohs(dsi->server.sin_port), srvloc_hostname);
409         }
410
411         err = SLPReg(hslp,
412                      dsi->srvloc_url,
413                      SLP_LIFETIME_MAXIMUM,
414                      "",
415                      "",
416                      SLP_TRUE,
417                      SRVLOC_callback,
418                      &callbackerr);
419         if (err != SLP_OK) {
420             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
421             dsi->srvloc_url[0] = '\0';
422             goto srvloc_reg_err;
423         }
424
425         if (callbackerr != SLP_OK) {
426             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
427             dsi->srvloc_url[0] = '\0';
428             goto srvloc_reg_err;
429         }
430
431         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
432
433 srvloc_reg_err:
434         SLPClose(hslp);
435     }
436 #endif /* USE_SRVLOC */
437
438
439     config->fd = dsi->serversock;
440     config->obj.handle = dsi;
441     config->obj.config = config;
442     config->obj.proto = AFPPROTO_DSI;
443
444     memcpy(&config->obj.options, options, sizeof(struct afp_options));
445     /* get rid of any appletalk info. we use the fact that the DSI
446      * stuff is done after the ASP stuff. */
447     p = config->obj.options.server;
448     if (p && (q = strchr(p, ':')))
449         *q = '\0';
450
451     config->optcount = refcount;
452     (*refcount)++;
453
454     config->server_start = dsi_start;
455 #ifdef USE_SRVLOC
456     config->server_cleanup = dsi_cleanup;
457 #endif 
458     return config;
459 }
460
461 /* allocate server configurations. this should really store the last
462  * entry in config->last or something like that. that would make
463  * supporting multiple dsi transports easier. */
464 static AFPConfig *AFPConfigInit(const struct afp_options *options,
465                                 const struct afp_options *defoptions)
466 {
467     AFPConfig *config = NULL, *next = NULL;
468     unsigned char *refcount;
469
470     if ((refcount = (unsigned char *)
471                     calloc(1, sizeof(unsigned char))) == NULL) {
472         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
473         return NULL;
474     }
475
476 #ifndef NO_DDP
477     /* handle asp transports */
478     if ((options->transports & AFPTRANS_DDP) &&
479             (config = ASPConfigInit(options, refcount)))
480         config->defoptions = defoptions;
481 #endif /* NO_DDP */
482
483     /* handle dsi transports and dsi proxies. we only proxy
484      * for DSI connections. */
485
486     /* this should have something like the following:
487      * for (i=mindsi; i < maxdsi; i++)
488      *   if (options->transports & (1 << i) && 
489      *     (next = DSIConfigInit(options, refcount, i)))
490      *     next->defoptions = defoptions;
491      */
492     if ((options->transports & AFPTRANS_TCP) &&
493             (((options->flags & OPTION_PROXY) == 0) ||
494              ((options->flags & OPTION_PROXY) && config))
495             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
496         next->defoptions = defoptions;
497
498     /* load in all the authentication modules. we can load the same
499        things multiple times if necessary. however, loading different
500        things with the same names will cause complaints. by not loading
501        in any uams with proxies, we prevent ddp connections from succeeding.
502     */
503     auth_load(options->uampath, options->uamlist);
504
505     /* this should be able to accept multiple dsi transports. i think
506      * the only thing that gets affected is the net addresses. */
507     status_init(config, next, options);
508
509     /* attach dsi config to tail of asp config */
510     if (config) {
511         config->next = next;
512         return config;
513     }
514
515     return next;
516 }
517
518 /* fill in the appropriate bits for each interface */
519 AFPConfig *configinit(struct afp_options *cmdline)
520 {
521     FILE *fp;
522     char buf[LINESIZE + 1], *p, have_option = 0;
523     struct afp_options options;
524     AFPConfig *config, *first = NULL;
525
526     status_reset();
527     /* if config file doesn't exist, load defaults */
528     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
529     {
530         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
531             cmdline->configfile);
532         return AFPConfigInit(cmdline, cmdline);
533     }
534
535     LOG(log_debug, logtype_afpd, "Loading ConfigFile"); 
536
537     /* scan in the configuration file */
538     while (!feof(fp)) {
539         if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
540             continue;
541
542         /* a little pre-processing to get rid of spaces and end-of-lines */
543         p = buf;
544         while (p && isspace(*p))
545             p++;
546         if (!p || (*p == '\0'))
547             continue;
548
549         have_option = 1;
550
551         memcpy(&options, cmdline, sizeof(options));
552         if (!afp_options_parseline(p, &options))
553             continue;
554
555         /* this should really get a head and a tail to simplify things. */
556         if (!first) {
557             if ((first = AFPConfigInit(&options, cmdline)))
558                 config = first->next ? first->next : first;
559         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
560             config = config->next->next ? config->next->next : config->next;
561         }
562     }
563
564     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
565     fclose(fp);
566
567     if (!have_option)
568         return AFPConfigInit(cmdline, cmdline);
569
570     return first;
571 }