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