]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
SLP support added (Joe Clarke)
[netatalk.git] / etc / afpd / afp_config.c
1 /* 
2  * $Id: afp_config.c,v 1.5 2001-09-17 13:41:26 rufustfirefly 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
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif /* HAVE_UNISTD_H */
33 #include <ctype.h>
34 #include <syslog.h>
35
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39
40 #include <atalk/dsi.h>
41 #include <atalk/atp.h>
42 #include <atalk/asp.h>
43 #include <atalk/nbp.h>
44 #include <atalk/afp.h>
45 #include <atalk/compat.h>
46 #include <atalk/server_child.h>
47 #ifdef USE_SRVLOC
48 #include <slp.h>
49 static char srvloc_url[512];
50 #endif
51
52 #include "globals.h"
53 #include "afp_config.h"
54 #include "uam_auth.h"
55 #include "status.h"
56
57 #define LINESIZE 1024  
58
59 /* get rid of unneeded configurations. i use reference counts to deal
60  * w/ multiple configs sharing the same afp_options. oh, to dream of
61  * garbage collection ... */
62 void configfree(AFPConfig *configs, const AFPConfig *config)
63 {
64   AFPConfig *p, *q;
65
66   for (p = configs; p; p = q) {
67     q = p->next;
68     if (p == config)
69       continue;
70
71     /* do a little reference counting */
72     if (--(*p->optcount) < 1) {
73       afp_options_free(&p->obj.options, p->defoptions);
74       free(p->optcount);
75     }
76
77     switch (p->obj.proto) {
78 #ifndef NO_DDP
79     case AFPPROTO_ASP:
80       free(p->obj.Obj);
81       free(p->obj.Type);
82       free(p->obj.Zone);
83       atp_close(((ASP) p->obj.handle)->asp_atp);
84       free(p->obj.handle);
85       break;
86 #endif /* no afp/asp */
87     case AFPPROTO_DSI:
88       close(p->fd);
89       free(p->obj.handle);
90       break;
91     }
92     free(p);
93   }
94 }
95
96 #ifdef USE_SRVLOC
97 static void SRVLOC_callback(SLPHandle hslp, SLPError errcode, void *cookie) {
98         *(SLPError*)cookie = errcode;
99 }
100 #endif
101
102 #ifdef USE_SRVLOC
103 static void dsi_cleanup(const AFPConfig *config)
104 {
105   SLPError err;
106   SLPError callbackerr;
107   SLPHandle hslp;
108   err = SLPOpen("en", SLP_FALSE, &hslp);
109   if (err != SLP_OK) {
110     syslog(LOG_ERR, "Error opening SRVLOC handle");
111     goto srvloc_dereg_err;
112   } 
113
114   err = SLPDereg(hslp,
115                  srvloc_url,
116                  SRVLOC_callback,
117                                  &callbackerr);
118   if (err != SLP_OK) {
119           syslog(LOG_ERR, "Error unregistering %s from SRVLOC", srvloc_url);
120           goto srvloc_dereg_err;
121   }
122
123   if (callbackerr != SLP_OK) {
124     syslog(LOG_ERR, "Error in callback while trying to unregister %s from SRVLOC (%i)", srvloc_url, callbackerr);
125         goto srvloc_dereg_err;
126   }
127
128   srvloc_dereg_err:
129     SLPClose(hslp);
130 }
131 #endif
132
133 #ifndef NO_DDP
134 static void asp_cleanup(const AFPConfig *config)
135 {
136   nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
137               &config->obj.options.ddpaddr);
138 }
139
140 /* these two are almost identical. it should be possible to collapse them
141  * into one with minimal junk. */
142 static int asp_start(AFPConfig *config, AFPConfig *configs, 
143                      server_child *server_children) 
144 {
145   ASP asp;
146
147   if (!(asp = asp_getsession(config->obj.handle, server_children, 
148                              config->obj.options.tickleval))) {
149     syslog( LOG_ERR, "main: asp_getsession: %m" );
150     exit( 1 );
151   } 
152   
153   if (asp->child) {
154     configfree(configs, config); /* free a bunch of stuff */
155     afp_over_asp(&config->obj);
156     exit (0);
157   }
158
159   return 0;
160 }
161 #endif /* no afp/asp */
162
163 static int dsi_start(AFPConfig *config, AFPConfig *configs,
164                      server_child *server_children)
165 {
166   DSI *dsi;
167
168   if (!(dsi = dsi_getsession(config->obj.handle, server_children,
169                              config->obj.options.tickleval))) {
170     syslog( LOG_ERR, "main: dsi_getsession: %m" );
171     exit( 1 );
172   } 
173   
174   /* we've forked. */
175   if (dsi->child) {
176     configfree(configs, config);
177     afp_over_dsi(&config->obj); /* start a session */
178     exit (0);
179   }
180
181   return 0;
182 }
183
184 #ifndef NO_DDP
185 static AFPConfig *ASPConfigInit(const struct afp_options *options,
186                                 unsigned char *refcount)
187 {
188   AFPConfig *config;
189   ATP atp;
190   ASP asp;
191   char *Obj, *Type = "AFPServer", *Zone = "*";
192   
193   if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
194     return NULL;
195   
196   if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
197     syslog( LOG_ERR, "main: atp_open: %m");
198     free(config);
199     return NULL;
200   }
201   
202   if ((asp = asp_init( atp )) == NULL) {
203     syslog( LOG_ERR, "main: asp_init: %m" );
204     atp_close(atp);
205     free(config);
206     return NULL;
207   }
208   
209   /* register asp server */
210   Obj = (char *) options->hostname;
211   if (nbp_name(options->server, &Obj, &Type, &Zone )) {
212     syslog( LOG_ERR, "main: can't parse %s", options->server );
213     goto serv_free_return;
214   }
215
216   /* dup Obj, Type and Zone as they get assigned to a single internal
217    * buffer by nbp_name */
218   if ((config->obj.Obj  = strdup(Obj)) == NULL) 
219     goto serv_free_return;
220
221   if ((config->obj.Type = strdup(Type)) == NULL) {
222     free(config->obj.Obj);
223     goto serv_free_return;
224   }
225
226   if ((config->obj.Zone = strdup(Zone)) == NULL) {
227     free(config->obj.Obj);
228     free(config->obj.Type);
229     goto serv_free_return;
230   }
231   
232   /* make sure we're not registered */
233   nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr); 
234   if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
235     syslog( LOG_ERR, "Can't register %s:%s@%s", Obj, Type, Zone );
236     free(config->obj.Obj);
237     free(config->obj.Type);
238     free(config->obj.Zone);
239     goto serv_free_return;
240   }
241
242   syslog( LOG_INFO, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
243           ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
244           atp_sockaddr( atp )->sat_addr.s_node,
245           atp_sockaddr( atp )->sat_port, VERSION );
246   
247   config->fd = atp_fileno(atp);
248   config->obj.handle = asp;
249   config->obj.config = config;
250   config->obj.proto = AFPPROTO_ASP;
251
252   memcpy(&config->obj.options, options, sizeof(struct afp_options));
253   config->optcount = refcount;
254   (*refcount)++;
255
256   config->server_start = asp_start;
257   config->server_cleanup = asp_cleanup;
258
259   return config;
260
261 serv_free_return:
262   asp_close(asp);
263   free(config);
264   return NULL;
265 }
266 #endif /* no afp/asp */
267
268
269 static AFPConfig *DSIConfigInit(const struct afp_options *options,
270                                 unsigned char *refcount,
271                                 const dsi_proto protocol) 
272 {
273   AFPConfig *config;
274   DSI *dsi;
275   char *p, *q;
276 #ifdef USE_SRVLOC
277   SLPError err;
278   SLPError callbackerr;
279   SLPHandle hslp;
280 #endif
281
282   if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
283     syslog( LOG_ERR, "DSIConfigInit: malloc(config): %m" );
284     return NULL;
285   }
286
287   if ((dsi = dsi_init(protocol, "afpd", options->hostname,
288                       options->ipaddr, options->port, 
289                       options->flags & OPTION_PROXY, 
290                       options->server_quantum)) == NULL) {
291     syslog( LOG_ERR, "main: dsi_init: %m" );
292     free(config);
293     return NULL;
294   }
295
296   if (options->flags & OPTION_PROXY) {
297     syslog(LOG_INFO, "ASIP proxy initialized for %s:%d (%s)",
298            inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), 
299            VERSION);
300   } else {
301     syslog(LOG_INFO, "ASIP started on %s:%d(%d) (%s)",
302            inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), 
303            dsi->serversock, VERSION);
304   }
305
306 #ifdef USE_SRVLOC
307   err = SLPOpen("en", SLP_FALSE, &hslp);
308   if (err != SLP_OK) {
309           syslog(LOG_ERR, "Error opening SRVLOC handle");
310           goto srvloc_reg_err;
311   }
312
313   snprintf(srvloc_url, sizeof(srvloc_url), "afp://%s:%d?NAME=%s", inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), options->hostname);
314
315   err = SLPReg(hslp,
316                srvloc_url,
317                            SLP_LIFETIME_MAXIMUM,
318                            "afp:",
319                            "",
320                            SLP_TRUE,
321                            SRVLOC_callback,
322                            &callbackerr);
323   if (err != SLP_OK) {
324           syslog(LOG_ERR, "Error registering %s with SRVLOC", srvloc_url);
325           goto srvloc_reg_err;
326   }
327
328   if (callbackerr != SLP_OK) {
329           syslog(LOG_ERR, "Error in callback trying to register %s with SRVLOC", srvloc_url);
330           goto srvloc_reg_err;
331   }
332
333   syslog(LOG_INFO, "Sucessfully registered %s with SRVLOC", srvloc_url);
334
335   srvloc_reg_err:
336     SLPClose(hslp);
337 #endif
338
339
340   config->fd = dsi->serversock;
341   config->obj.handle = dsi;
342   config->obj.config = config;
343   config->obj.proto = AFPPROTO_DSI;
344
345   memcpy(&config->obj.options, options, sizeof(struct afp_options));
346   /* get rid of any appletalk info. we use the fact that the DSI
347    * stuff is done after the ASP stuff. */
348   p = config->obj.options.server;
349   if (p && (q = strchr(p, ':')))
350     *q = '\0';
351     
352   config->optcount = refcount;
353   (*refcount)++;
354
355   config->server_start = dsi_start;
356 #ifdef USE_SRVLOC
357   config->server_cleanup = dsi_cleanup;
358 #endif
359   return config;
360 }
361
362 /* allocate server configurations. this should really store the last
363  * entry in config->last or something like that. that would make
364  * supporting multiple dsi transports easier. */
365 static AFPConfig *AFPConfigInit(const struct afp_options *options,
366                                 const struct afp_options *defoptions)
367 {
368   AFPConfig *config = NULL, *next = NULL;
369   unsigned char *refcount;
370
371   if ((refcount = (unsigned char *) 
372        calloc(1, sizeof(unsigned char))) == NULL) {
373     syslog( LOG_ERR, "AFPConfigInit: calloc(refcount): %m" );
374     return NULL;
375   }
376
377 #ifndef NO_DDP
378   /* handle asp transports */
379   if ((options->transports & AFPTRANS_DDP) && 
380       (config = ASPConfigInit(options, refcount)))
381     config->defoptions = defoptions;
382 #endif /* NO_DDP */
383
384   /* handle dsi transports and dsi proxies. we only proxy
385    * for DSI connections. */
386
387   /* this should have something like the following:
388    * for (i=mindsi; i < maxdsi; i++)
389    *   if (options->transports & (1 << i) && 
390    *     (next = DSIConfigInit(options, refcount, i)))
391    *     next->defoptions = defoptions;
392    */
393   if ((options->transports & AFPTRANS_TCP) &&
394       (((options->flags & OPTION_PROXY) == 0) ||
395        ((options->flags & OPTION_PROXY) && config))
396       && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
397     next->defoptions = defoptions;
398
399   /* load in all the authentication modules. we can load the same
400      things multiple times if necessary. however, loading different
401      things with the same names will cause complaints. by not loading
402      in any uams with proxies, we prevent ddp connections from succeeding.
403   */
404   auth_load(options->uampath, options->uamlist);
405
406   /* this should be able to accept multiple dsi transports. i think
407    * the only thing that gets affected is the net addresses. */
408   status_init(config, next, options);
409
410   /* attach dsi config to tail of asp config */
411   if (config) {
412     config->next = next;
413     return config;
414   } 
415     
416   return next;
417 }
418
419 /* fill in the appropriate bits for each interface */
420 AFPConfig *configinit(struct afp_options *cmdline)
421 {
422   FILE *fp;
423   char buf[LINESIZE + 1], *p, have_option = 0;
424   struct afp_options options;
425   AFPConfig *config, *first = NULL;
426
427   /* if config file doesn't exist, load defaults */
428   if ((fp = fopen(cmdline->configfile, "r")) == NULL) 
429     return AFPConfigInit(cmdline, cmdline);
430
431   /* scan in the configuration file */
432   while (!feof(fp)) {
433     if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
434       continue;
435     
436     /* a little pre-processing to get rid of spaces and end-of-lines */
437     p = buf;
438     while (p && isspace(*p)) 
439       p++;
440     if (!p || (*p == '\0'))
441       continue;
442
443     have_option = 1;
444
445     memcpy(&options, cmdline, sizeof(options));
446     if (!afp_options_parseline(p, &options))
447       continue;
448
449     /* this should really get a head and a tail to simplify things. */
450     if (!first) {
451       if ((first = AFPConfigInit(&options, cmdline)))
452         config = first->next ? first->next : first;
453     } else if ((config->next = AFPConfigInit(&options, cmdline))) {
454       config = config->next->next ? config->next->next : config->next;
455     }
456   }
457
458   fclose(fp);
459
460   if (!have_option)
461     return AFPConfigInit(cmdline, cmdline);
462
463   return first;
464 }