]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
Fixed a warning.
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * $Id: afp_config.c,v 1.21 2002-09-12 17:33:03 srittau 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
111     /*  Do nothing if we didn't register.  */
112     if (srvloc_url[0] == '\0')
113         return;
114
115     err = SLPOpen("en", SLP_FALSE, &hslp);
116     if (err != SLP_OK) {
117         LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
118         goto srvloc_dereg_err;
119     }
120
121     err = SLPDereg(hslp,
122                    srvloc_url,
123                    SRVLOC_callback,
124                    &callbackerr);
125     if (err != SLP_OK) {
126         LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", srvloc_url);
127         goto srvloc_dereg_err;
128     }
129
130     if (callbackerr != SLP_OK) {
131         LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", srvloc_url, callbackerr);
132         goto srvloc_dereg_err;
133     }
134
135 srvloc_dereg_err:
136     SLPClose(hslp);
137 }
138 #endif /* USE_SRVLOC */
139
140 #ifndef NO_DDP
141 static void asp_cleanup(const AFPConfig *config)
142 {
143     nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
144                 &config->obj.options.ddpaddr);
145 }
146
147 /* these two are almost identical. it should be possible to collapse them
148  * into one with minimal junk. */
149 static int asp_start(AFPConfig *config, AFPConfig *configs,
150                      server_child *server_children)
151 {
152     ASP asp;
153
154     if (!(asp = asp_getsession(config->obj.handle, server_children,
155                                config->obj.options.tickleval))) {
156         LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
157         exit( 1 );
158     }
159
160     if (asp->child) {
161         configfree(configs, config); /* free a bunch of stuff */
162         afp_over_asp(&config->obj);
163         exit (0);
164     }
165
166     return 0;
167 }
168 #endif /* no afp/asp */
169
170 static int dsi_start(AFPConfig *config, AFPConfig *configs,
171                      server_child *server_children)
172 {
173     DSI *dsi;
174
175     if (!(dsi = dsi_getsession(config->obj.handle, server_children,
176                                config->obj.options.tickleval))) {
177         LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
178         exit( 1 );
179     }
180
181     /* we've forked. */
182     if (dsi->child) {
183         configfree(configs, config);
184         afp_over_dsi(&config->obj); /* start a session */
185         exit (0);
186     }
187
188     return 0;
189 }
190
191 #ifndef NO_DDP
192 static AFPConfig *ASPConfigInit(const struct afp_options *options,
193                                 unsigned char *refcount)
194 {
195     AFPConfig *config;
196     ATP atp;
197     ASP asp;
198     char *Obj, *Type = "AFPServer", *Zone = "*";
199
200     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
201         return NULL;
202
203     if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
204         LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
205         free(config);
206         return NULL;
207     }
208
209     if ((asp = asp_init( atp )) == NULL) {
210         LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
211         atp_close(atp);
212         free(config);
213         return NULL;
214     }
215
216     /* register asp server */
217     Obj = (char *) options->hostname;
218     if (nbp_name(options->server, &Obj, &Type, &Zone )) {
219         LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
220         goto serv_free_return;
221     }
222
223     /* dup Obj, Type and Zone as they get assigned to a single internal
224      * buffer by nbp_name */
225     if ((config->obj.Obj  = strdup(Obj)) == NULL)
226         goto serv_free_return;
227
228     if ((config->obj.Type = strdup(Type)) == NULL) {
229         free(config->obj.Obj);
230         goto serv_free_return;
231     }
232
233     if ((config->obj.Zone = strdup(Zone)) == NULL) {
234         free(config->obj.Obj);
235         free(config->obj.Type);
236         goto serv_free_return;
237     }
238
239     /* make sure we're not registered */
240     nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
241     if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
242         LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
243         free(config->obj.Obj);
244         free(config->obj.Type);
245         free(config->obj.Zone);
246         goto serv_free_return;
247     }
248
249     LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
250         ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
251         atp_sockaddr( atp )->sat_addr.s_node,
252         atp_sockaddr( atp )->sat_port, VERSION );
253
254     config->fd = atp_fileno(atp);
255     config->obj.handle = asp;
256     config->obj.config = config;
257     config->obj.proto = AFPPROTO_ASP;
258
259     memcpy(&config->obj.options, options, sizeof(struct afp_options));
260     config->optcount = refcount;
261     (*refcount)++;
262
263     config->server_start = asp_start;
264     config->server_cleanup = asp_cleanup;
265
266     return config;
267
268 serv_free_return:
269                     asp_close(asp);
270     free(config);
271     return NULL;
272 }
273 #endif /* no afp/asp */
274
275
276 static AFPConfig *DSIConfigInit(const struct afp_options *options,
277                                 unsigned char *refcount,
278                                 const dsi_proto protocol)
279 {
280     AFPConfig *config;
281     DSI *dsi;
282     char *p, *q;
283 #ifdef USE_SRVLOC
284     SLPError err;
285     SLPError callbackerr;
286     SLPHandle hslp;
287     struct servent *afpovertcp;
288     int afp_port = 548;
289     const char *srvloc_hostname;
290 #endif /* USE_SRVLOC */
291
292     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
293         LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
294         return NULL;
295     }
296
297     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
298                         options->ipaddr, options->port,
299                         options->flags & OPTION_PROXY,
300                         options->server_quantum)) == NULL) {
301         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
302         free(config);
303         return NULL;
304     }
305
306     if (options->flags & OPTION_PROXY) {
307         LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
308             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
309             VERSION);
310     } else {
311         LOG(log_info, logtype_afpd, "ASIP started on %s:%d(%d) (%s)",
312             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
313             dsi->serversock, VERSION);
314     }
315
316 #ifdef USE_SRVLOC
317     srvloc_url[0] = '\0';       /*  Mark that we haven't registered.  */
318     if (!(options->flags & OPTION_NOSLP)) {
319         err = SLPOpen("en", SLP_FALSE, &hslp);
320         if (err != SLP_OK) {
321             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
322             goto srvloc_reg_err;
323         }
324
325         /* XXX We don't want to tack on the port number if we don't have to.
326          * Why?
327          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
328          * use a non-default port, they can, but be aware, this server might
329          * not show up int the Network Browser.
330          */
331         afpovertcp = getservbyname("afpovertcp", "tcp");
332         srvloc_hostname = (options->server ? options->server : options->hostname);
333         if (afpovertcp != NULL) {
334             afp_port = afpovertcp->s_port;
335         }
336         if (strlen(srvloc_hostname) > (sizeof(srvloc_url) - strlen(inet_ntoa(dsi->server.sin_addr)) - 21)) {
337             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
338             srvloc_url[0] = '\0';
339             goto srvloc_reg_err;
340         }
341         if (dsi->server.sin_port == afp_port) {
342             sprintf(srvloc_url, "afp://%s/?NAME=%s", inet_ntoa(dsi->server.sin_addr), srvloc_hostname);
343         }
344         else {
345             sprintf(srvloc_url, "afp://%s:%d/?NAME=%s", inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), srvloc_hostname);
346         }
347
348         err = SLPReg(hslp,
349                      srvloc_url,
350                      SLP_LIFETIME_MAXIMUM,
351                      "",
352                      "",
353                      SLP_TRUE,
354                      SRVLOC_callback,
355                      &callbackerr);
356         if (err != SLP_OK) {
357             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", srvloc_url);
358             srvloc_url[0] = '\0';
359             goto srvloc_reg_err;
360         }
361
362         if (callbackerr != SLP_OK) {
363             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", srvloc_url);
364             srvloc_url[0] = '\0';
365             goto srvloc_reg_err;
366         }
367
368         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", srvloc_url);
369
370 srvloc_reg_err:
371         SLPClose(hslp);
372     }
373 #endif /* USE_SRVLOC */
374
375
376     config->fd = dsi->serversock;
377     config->obj.handle = dsi;
378     config->obj.config = config;
379     config->obj.proto = AFPPROTO_DSI;
380
381     memcpy(&config->obj.options, options, sizeof(struct afp_options));
382     /* get rid of any appletalk info. we use the fact that the DSI
383      * stuff is done after the ASP stuff. */
384     p = config->obj.options.server;
385     if (p && (q = strchr(p, ':')))
386         *q = '\0';
387
388     config->optcount = refcount;
389     (*refcount)++;
390
391     config->server_start = dsi_start;
392 #ifdef USE_SRVLOC
393     config->server_cleanup = dsi_cleanup;
394 #endif /* USE_SRVLOC */
395     return config;
396 }
397
398 /* allocate server configurations. this should really store the last
399  * entry in config->last or something like that. that would make
400  * supporting multiple dsi transports easier. */
401 static AFPConfig *AFPConfigInit(const struct afp_options *options,
402                                 const struct afp_options *defoptions)
403 {
404     AFPConfig *config = NULL, *next = NULL;
405     unsigned char *refcount;
406
407     if ((refcount = (unsigned char *)
408                     calloc(1, sizeof(unsigned char))) == NULL) {
409         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
410         return NULL;
411     }
412
413 #ifndef NO_DDP
414     /* handle asp transports */
415     if ((options->transports & AFPTRANS_DDP) &&
416             (config = ASPConfigInit(options, refcount)))
417         config->defoptions = defoptions;
418 #endif /* NO_DDP */
419
420     /* handle dsi transports and dsi proxies. we only proxy
421      * for DSI connections. */
422
423     /* this should have something like the following:
424      * for (i=mindsi; i < maxdsi; i++)
425      *   if (options->transports & (1 << i) && 
426      *     (next = DSIConfigInit(options, refcount, i)))
427      *     next->defoptions = defoptions;
428      */
429     if ((options->transports & AFPTRANS_TCP) &&
430             (((options->flags & OPTION_PROXY) == 0) ||
431              ((options->flags & OPTION_PROXY) && config))
432             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
433         next->defoptions = defoptions;
434
435     /* load in all the authentication modules. we can load the same
436        things multiple times if necessary. however, loading different
437        things with the same names will cause complaints. by not loading
438        in any uams with proxies, we prevent ddp connections from succeeding.
439     */
440     auth_load(options->uampath, options->uamlist);
441
442     /* this should be able to accept multiple dsi transports. i think
443      * the only thing that gets affected is the net addresses. */
444     status_init(config, next, options);
445
446     /* attach dsi config to tail of asp config */
447     if (config) {
448         config->next = next;
449         return config;
450     }
451
452     return next;
453 }
454
455 /* fill in the appropriate bits for each interface */
456 AFPConfig *configinit(struct afp_options *cmdline)
457 {
458     FILE *fp;
459     char buf[LINESIZE + 1], *p, have_option = 0;
460     struct afp_options options;
461     AFPConfig *config, *first = NULL;
462
463     /* if config file doesn't exist, load defaults */
464     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
465     {
466         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
467             cmdline->configfile);
468         return AFPConfigInit(cmdline, cmdline);
469     }
470
471     LOG(log_debug, logtype_afpd, "Loading ConfigFile"); 
472
473     /* scan in the configuration file */
474     while (!feof(fp)) {
475         if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
476             continue;
477
478         /* a little pre-processing to get rid of spaces and end-of-lines */
479         p = buf;
480         while (p && isspace(*p))
481             p++;
482         if (!p || (*p == '\0'))
483             continue;
484
485         have_option = 1;
486
487         memcpy(&options, cmdline, sizeof(options));
488         if (!afp_options_parseline(p, &options))
489             continue;
490
491         /* this should really get a head and a tail to simplify things. */
492         if (!first) {
493             if ((first = AFPConfigInit(&options, cmdline)))
494                 config = first->next ? first->next : first;
495         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
496             config = config->next->next ? config->next->next : config->next;
497         }
498     }
499
500     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
501     fclose(fp);
502
503     if (!have_option)
504         return AFPConfigInit(cmdline, cmdline);
505
506     return first;
507 }