/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/mandosclient.c

  • Committer: Björn Påhlsson
  • Date: 2007-10-20 21:38:25 UTC
  • Revision ID: belorn@tower-20071020213825-abf6f0d1c33ee961
First working version with: IPv6, GnuTLS, X.509 certificates, DN
retrieval.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/***
2
 
  This file is part of avahi.
3
 
 
4
 
  avahi is free software; you can redistribute it and/or modify it
5
 
  under the terms of the GNU Lesser General Public License as
6
 
  published by the Free Software Foundation; either version 2.1 of the
7
 
  License, or (at your option) any later version.
8
 
 
9
 
  avahi is distributed in the hope that it will be useful, but WITHOUT
10
 
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
 
  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12
 
  Public License for more details.
13
 
 
14
 
  You should have received a copy of the GNU Lesser General Public
15
 
  License along with avahi; if not, write to the Free Software
16
 
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17
 
  USA.
18
 
***/
19
 
 
20
 
#define _LARGEFILE_SOURCE
21
 
#define _FILE_OFFSET_BITS 64
22
 
 
23
 
#include <stdio.h>
24
 
#include <assert.h>
25
 
#include <stdlib.h>
26
 
#include <time.h>
27
 
#include <net/if.h>             /* if_nametoindex */
28
 
 
29
 
#include <avahi-core/core.h>
30
 
#include <avahi-core/lookup.h>
31
 
#include <avahi-core/log.h>
32
 
#include <avahi-common/simple-watch.h>
33
 
#include <avahi-common/malloc.h>
34
 
#include <avahi-common/error.h>
35
 
 
36
 
//mandos client part
37
 
#include <sys/types.h>          /* socket(), setsockopt(), inet_pton() */
38
 
#include <sys/socket.h>         /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
39
 
#include <gnutls/gnutls.h>      /* ALL GNUTLS STUFF */
40
 
#include <gnutls/openpgp.h>     /* gnutls with openpgp stuff */
41
 
 
42
 
#include <unistd.h>             /* close() */
43
 
#include <netinet/in.h>
44
 
#include <stdbool.h>            /* true */
45
 
#include <string.h>             /* memset */
46
 
#include <arpa/inet.h>          /* inet_pton() */
47
 
#include <iso646.h>             /* not */
48
 
 
49
 
// gpgme
50
 
#include <errno.h>              /* perror() */
51
 
#include <gpgme.h>
52
 
 
53
 
// getopt long
54
 
#include <getopt.h>
55
 
 
56
 
#ifndef CERT_ROOT
57
 
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
58
 
#endif
59
 
#define CERTFILE CERT_ROOT "openpgp-client.txt"
60
 
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
61
 
#define BUFFER_SIZE 256
62
 
#define DH_BITS 1024
63
 
 
64
 
bool debug = false;
65
 
char *interface = "eth0";
66
 
 
67
 
typedef struct {
68
 
  gnutls_session_t session;
69
 
  gnutls_certificate_credentials_t cred;
70
 
  gnutls_dh_params_t dh_params;
71
 
} encrypted_session;
72
 
 
73
 
 
74
 
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
75
 
  gpgme_data_t dh_crypto, dh_plain;
76
 
  gpgme_ctx_t ctx;
77
 
  gpgme_error_t rc;
78
 
  ssize_t ret;
79
 
  size_t new_packet_capacity = 0;
80
 
  size_t new_packet_length = 0;
81
 
  gpgme_engine_info_t engine_info;
82
 
 
83
 
  if (debug){
84
 
    fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
85
 
  }
86
 
  
87
 
  /* Init GPGME */
88
 
  gpgme_check_version(NULL);
89
 
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
90
 
  
91
 
  /* Set GPGME home directory */
92
 
  rc = gpgme_get_engine_info (&engine_info);
93
 
  if (rc != GPG_ERR_NO_ERROR){
94
 
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
95
 
            gpgme_strsource(rc), gpgme_strerror(rc));
96
 
    return -1;
97
 
  }
98
 
  while(engine_info != NULL){
99
 
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
100
 
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
101
 
                            engine_info->file_name, homedir);
102
 
      break;
103
 
    }
104
 
    engine_info = engine_info->next;
105
 
  }
106
 
  if(engine_info == NULL){
107
 
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
108
 
    return -1;
109
 
  }
110
 
  
111
 
  /* Create new GPGME data buffer from packet buffer */
112
 
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
113
 
  if (rc != GPG_ERR_NO_ERROR){
114
 
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
115
 
            gpgme_strsource(rc), gpgme_strerror(rc));
116
 
    return -1;
117
 
  }
118
 
  
119
 
  /* Create new empty GPGME data buffer for the plaintext */
120
 
  rc = gpgme_data_new(&dh_plain);
121
 
  if (rc != GPG_ERR_NO_ERROR){
122
 
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
123
 
            gpgme_strsource(rc), gpgme_strerror(rc));
124
 
    return -1;
125
 
  }
126
 
  
127
 
  /* Create new GPGME "context" */
128
 
  rc = gpgme_new(&ctx);
129
 
  if (rc != GPG_ERR_NO_ERROR){
130
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
131
 
            gpgme_strsource(rc), gpgme_strerror(rc));
132
 
    return -1;
133
 
  }
134
 
  
135
 
  /* Decrypt data from the FILE pointer to the plaintext data buffer */
136
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
137
 
  if (rc != GPG_ERR_NO_ERROR){
138
 
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
139
 
            gpgme_strsource(rc), gpgme_strerror(rc));
140
 
    return -1;
141
 
  }
142
 
 
143
 
  if(debug){
144
 
    fprintf(stderr, "decryption of gpg packet succeeded\n");
145
 
  }
146
 
 
147
 
  if (debug){
148
 
    gpgme_decrypt_result_t result;
149
 
    result = gpgme_op_decrypt_result(ctx);
150
 
    if (result == NULL){
151
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
152
 
    } else {
153
 
      fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm);
154
 
      fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage);
155
 
      if(result->file_name != NULL){
156
 
        fprintf(stderr, "File name: %s\n", result->file_name);
157
 
      }
158
 
      gpgme_recipient_t recipient;
159
 
      recipient = result->recipients;
160
 
      if(recipient){
161
 
        while(recipient != NULL){
162
 
          fprintf(stderr, "Public key algorithm: %s\n",
163
 
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
164
 
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
165
 
          fprintf(stderr, "Secret key available: %s\n",
166
 
                  recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
167
 
          recipient = recipient->next;
168
 
        }
169
 
      }
170
 
    }
171
 
  }
172
 
  
173
 
  /* Delete the GPGME FILE pointer cryptotext data buffer */
174
 
  gpgme_data_release(dh_crypto);
175
 
  
176
 
  /* Seek back to the beginning of the GPGME plaintext data buffer */
177
 
  gpgme_data_seek(dh_plain, 0, SEEK_SET);
178
 
 
179
 
  *new_packet = 0;
180
 
  while(true){
181
 
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
182
 
      *new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
183
 
      if (*new_packet == NULL){
184
 
        perror("realloc");
185
 
        return -1;
186
 
      }
187
 
      new_packet_capacity += BUFFER_SIZE;
188
 
    }
189
 
    
190
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
191
 
    /* Print the data, if any */
192
 
    if (ret == 0){
193
 
      /* If password is empty, then a incorrect error will be printed */
194
 
      break;
195
 
    }
196
 
    if(ret < 0){
197
 
      perror("gpgme_data_read");
198
 
      return -1;
199
 
    }
200
 
    new_packet_length += ret;
201
 
  }
202
 
 
203
 
  /* FIXME: check characters before printing to screen so to not print
204
 
     terminal control characters */
205
 
  /*   if(debug){ */
206
 
  /*     fprintf(stderr, "decrypted password is: "); */
207
 
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
208
 
  /*     fprintf(stderr, "\n"); */
209
 
  /*   } */
210
 
  
211
 
  /* Delete the GPGME plaintext data buffer */
212
 
  gpgme_data_release(dh_plain);
213
 
  return new_packet_length;
214
 
}
215
 
 
216
 
static const char * safer_gnutls_strerror (int value) {
217
 
  const char *ret = gnutls_strerror (value);
218
 
  if (ret == NULL)
219
 
    ret = "(unknown)";
220
 
  return ret;
221
 
}
222
 
 
223
 
void debuggnutls(int level, const char* string){
224
 
  fprintf(stderr, "%s", string);
225
 
}
226
 
 
227
 
int initgnutls(encrypted_session *es){
228
 
  const char *err;
229
 
  int ret;
230
 
 
231
 
  if(debug){
232
 
    fprintf(stderr, "Initializing gnutls\n");
233
 
  }
234
 
 
235
 
  
236
 
  if ((ret = gnutls_global_init ())
237
 
      != GNUTLS_E_SUCCESS) {
238
 
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
239
 
    return -1;
240
 
  }
241
 
 
242
 
  if (debug){
243
 
    gnutls_global_set_log_level(11);
244
 
    gnutls_global_set_log_function(debuggnutls);
245
 
  }
246
 
  
247
 
 
248
 
  /* openpgp credentials */
249
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
250
 
      != GNUTLS_E_SUCCESS) {
251
 
    fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
252
 
    return -1;
253
 
  }
254
 
 
255
 
  if(debug){
256
 
    fprintf(stderr, "Attempting to use openpgp certificate %s"
257
 
            " and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
258
 
  }
259
 
 
260
 
  ret = gnutls_certificate_set_openpgp_key_file
261
 
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
262
 
  if (ret != GNUTLS_E_SUCCESS) {
263
 
    fprintf
264
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
265
 
       ret, CERTFILE, KEYFILE);
266
 
    fprintf(stdout, "The Error is: %s\n",
267
 
            safer_gnutls_strerror(ret));
268
 
    return -1;
269
 
  }
270
 
 
271
 
  //Gnutls server initialization
272
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
273
 
      != GNUTLS_E_SUCCESS) {
274
 
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
275
 
             safer_gnutls_strerror(ret));
276
 
    return -1;
277
 
  }
278
 
 
279
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
280
 
      != GNUTLS_E_SUCCESS) {
281
 
    fprintf (stderr, "Error in prime generation: %s\n",
282
 
             safer_gnutls_strerror(ret));
283
 
    return -1;
284
 
  }
285
 
 
286
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
287
 
 
288
 
  // Gnutls session creation
289
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
290
 
      != GNUTLS_E_SUCCESS){
291
 
    fprintf(stderr, "Error in gnutls session initialization: %s\n",
292
 
            safer_gnutls_strerror(ret));
293
 
  }
294
 
 
295
 
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
296
 
      != GNUTLS_E_SUCCESS) {
297
 
    fprintf(stderr, "Syntax error at: %s\n", err);
298
 
    fprintf(stderr, "Gnutls error: %s\n",
299
 
            safer_gnutls_strerror(ret));
300
 
    return -1;
301
 
  }
302
 
 
303
 
  if ((ret = gnutls_credentials_set
304
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
305
 
      != GNUTLS_E_SUCCESS) {
306
 
    fprintf(stderr, "Error setting a credentials set: %s\n",
307
 
            safer_gnutls_strerror(ret));
308
 
    return -1;
309
 
  }
310
 
 
311
 
  /* ignore client certificate if any. */
312
 
  gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
313
 
  
314
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
315
 
  
316
 
  return 0;
317
 
}
318
 
 
319
 
void empty_log(AvahiLogLevel level, const char *txt){}
320
 
 
321
 
int start_mandos_communcation(char *ip, uint16_t port){
322
 
  int ret, tcp_sd;
323
 
  struct sockaddr_in6 to;
324
 
  struct in6_addr ip_addr;
325
 
  encrypted_session es;
326
 
  char *buffer = NULL;
327
 
  char *decrypted_buffer;
328
 
  size_t buffer_length = 0;
329
 
  size_t buffer_capacity = 0;
330
 
  ssize_t decrypted_buffer_size;
331
 
  int retval = 0;
332
 
 
333
 
  if(debug){
334
 
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
335
 
  }
336
 
  
337
 
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
338
 
  if(tcp_sd < 0) {
339
 
    perror("socket");
340
 
    return -1;
341
 
  }
342
 
 
343
 
  if(debug){
344
 
    fprintf(stderr, "Binding to interface %s\n", interface);
345
 
  }
346
 
 
347
 
  ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
348
 
  if(tcp_sd < 0) {
349
 
    perror("setsockopt bindtodevice");
350
 
    return -1;
351
 
  }
352
 
  
353
 
  memset(&to,0,sizeof(to));
354
 
  to.sin6_family = AF_INET6;
355
 
  ret = inet_pton(AF_INET6, ip, &ip_addr);
356
 
  if (ret < 0 ){
357
 
    perror("inet_pton");
358
 
    return -1;
359
 
  }  
360
 
  if(ret == 0){
361
 
    fprintf(stderr, "Bad address: %s\n", ip);
362
 
    return -1;
363
 
  }
364
 
  to.sin6_port = htons(port);
365
 
  to.sin6_scope_id = if_nametoindex(interface);
366
 
 
367
 
  if(debug){
368
 
    fprintf(stderr, "Connection to: %s\n", ip);
369
 
  }
370
 
  
371
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
372
 
  if (ret < 0){
373
 
    perror("connect");
374
 
    return -1;
375
 
  }
376
 
  
377
 
  ret = initgnutls (&es);
378
 
  if (ret != 0){
379
 
    retval = -1;
380
 
    return -1;
381
 
  }
382
 
    
383
 
  
384
 
  gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
385
 
 
386
 
  if(debug){
387
 
    fprintf(stderr, "Establishing tls session with %s\n", ip);
388
 
  }
389
 
 
390
 
  
391
 
  ret = gnutls_handshake (es.session);
392
 
  
393
 
  if (ret != GNUTLS_E_SUCCESS){
394
 
    fprintf(stderr, "\n*** Handshake failed ***\n");
395
 
    gnutls_perror (ret);
396
 
    retval = -1;
397
 
    goto exit;
398
 
  }
399
 
 
400
 
  //Retrieve gpg packet that contains the wanted password
401
 
 
402
 
  if(debug){
403
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
404
 
  }
405
 
 
406
 
  while(true){
407
 
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
408
 
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
409
 
      if (buffer == NULL){
410
 
        perror("realloc");
411
 
        goto exit;
412
 
      }
413
 
      buffer_capacity += BUFFER_SIZE;
414
 
    }
415
 
    
416
 
    ret = gnutls_record_recv
417
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
418
 
    if (ret == 0){
419
 
      break;
420
 
    }
421
 
    if (ret < 0){
422
 
      switch(ret){
423
 
      case GNUTLS_E_INTERRUPTED:
424
 
      case GNUTLS_E_AGAIN:
425
 
        break;
426
 
      case GNUTLS_E_REHANDSHAKE:
427
 
        ret = gnutls_handshake (es.session);
428
 
        if (ret < 0){
429
 
          fprintf(stderr, "\n*** Handshake failed ***\n");
430
 
          gnutls_perror (ret);
431
 
          retval = -1;
432
 
          goto exit;
433
 
        }
434
 
        break;
435
 
      default:
436
 
        fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
437
 
        retval = -1;
438
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
439
 
        goto exit;
440
 
      }
441
 
    } else {
442
 
      buffer_length += ret;
443
 
    }
444
 
  }
445
 
  
446
 
  if (buffer_length > 0){
447
 
    if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
448
 
      fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
449
 
      free(decrypted_buffer);
450
 
    } else {
451
 
      retval = -1;
452
 
    }
453
 
  }
454
 
 
455
 
  //shutdown procedure
456
 
 
457
 
  if(debug){
458
 
    fprintf(stderr, "Closing tls session\n");
459
 
  }
460
 
 
461
 
  free(buffer);
462
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
463
 
 exit:
464
 
  close(tcp_sd);
465
 
  gnutls_deinit (es.session);
466
 
  gnutls_certificate_free_credentials (es.cred);
467
 
  gnutls_global_deinit ();
468
 
  return retval;
469
 
}
470
 
 
471
 
static AvahiSimplePoll *simple_poll = NULL;
472
 
static AvahiServer *server = NULL;
473
 
 
474
 
static void resolve_callback(
475
 
    AvahiSServiceResolver *r,
476
 
    AVAHI_GCC_UNUSED AvahiIfIndex interface,
477
 
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
478
 
    AvahiResolverEvent event,
479
 
    const char *name,
480
 
    const char *type,
481
 
    const char *domain,
482
 
    const char *host_name,
483
 
    const AvahiAddress *address,
484
 
    uint16_t port,
485
 
    AvahiStringList *txt,
486
 
    AvahiLookupResultFlags flags,
487
 
    AVAHI_GCC_UNUSED void* userdata) {
488
 
    
489
 
    assert(r);
490
 
 
491
 
    /* Called whenever a service has been resolved successfully or timed out */
492
 
 
493
 
    switch (event) {
494
 
        case AVAHI_RESOLVER_FAILURE:
495
 
            fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_server_errno(server)));
496
 
            break;
497
 
 
498
 
        case AVAHI_RESOLVER_FOUND: {
499
 
          char ip[AVAHI_ADDRESS_STR_MAX];
500
 
            avahi_address_snprint(ip, sizeof(ip), address);
501
 
            if(debug){
502
 
              fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
503
 
            }
504
 
            int ret = start_mandos_communcation(ip, port);
505
 
            if (ret == 0){
506
 
              exit(EXIT_SUCCESS);
507
 
            } else {
508
 
              exit(EXIT_FAILURE);
509
 
            }
510
 
        }
511
 
    }
512
 
    avahi_s_service_resolver_free(r);
513
 
}
514
 
 
515
 
static void browse_callback(
516
 
    AvahiSServiceBrowser *b,
517
 
    AvahiIfIndex interface,
518
 
    AvahiProtocol protocol,
519
 
    AvahiBrowserEvent event,
520
 
    const char *name,
521
 
    const char *type,
522
 
    const char *domain,
523
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
524
 
    void* userdata) {
525
 
    
526
 
    AvahiServer *s = userdata;
527
 
    assert(b);
528
 
 
529
 
    /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
530
 
 
531
 
    switch (event) {
532
 
 
533
 
        case AVAHI_BROWSER_FAILURE:
534
 
            
535
 
            fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
536
 
            avahi_simple_poll_quit(simple_poll);
537
 
            return;
538
 
 
539
 
        case AVAHI_BROWSER_NEW:
540
 
            /* We ignore the returned resolver object. In the callback
541
 
               function we free it. If the server is terminated before
542
 
               the callback function is called the server will free
543
 
               the resolver for us. */
544
 
            
545
 
            if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
546
 
                fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
547
 
            
548
 
            break;
549
 
 
550
 
        case AVAHI_BROWSER_REMOVE:
551
 
            break;
552
 
 
553
 
        case AVAHI_BROWSER_ALL_FOR_NOW:
554
 
        case AVAHI_BROWSER_CACHE_EXHAUSTED:
555
 
            break;
556
 
    }
557
 
}
558
 
 
559
 
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
560
 
    AvahiServerConfig config;
561
 
    AvahiSServiceBrowser *sb = NULL;
562
 
    int error;
563
 
    int ret;
564
 
    int returncode = EXIT_SUCCESS;
565
 
 
566
 
    while (true){
567
 
      static struct option long_options[] = {
568
 
        {"debug", no_argument, (int *)&debug, 1},
569
 
        {"interface", required_argument, 0, 'i'},
570
 
        {0, 0, 0, 0} };
571
 
 
572
 
      int option_index = 0;
573
 
      ret = getopt_long (argc, argv, "i:", long_options, &option_index);
574
 
 
575
 
      if (ret == -1){
576
 
        break;
577
 
      }
578
 
      
579
 
      switch(ret){
580
 
      case 0:
581
 
        break;
582
 
      case 'i':
583
 
        interface = optarg;
584
 
        break;
585
 
      default:
586
 
        exit(EXIT_FAILURE);
587
 
      }
588
 
    }
589
 
    
590
 
    if (not debug){
591
 
      avahi_set_log_function(empty_log);
592
 
    }
593
 
    
594
 
    /* Initialize the psuedo-RNG */
595
 
    srand(time(NULL));
596
 
 
597
 
    /* Allocate main loop object */
598
 
    if (!(simple_poll = avahi_simple_poll_new())) {
599
 
        fprintf(stderr, "Failed to create simple poll object.\n");
600
 
        
601
 
        goto exit;
602
 
    }
603
 
 
604
 
    /* Do not publish any local records */
605
 
    avahi_server_config_init(&config);
606
 
    config.publish_hinfo = 0;
607
 
    config.publish_addresses = 0;
608
 
    config.publish_workstation = 0;
609
 
    config.publish_domain = 0;
610
 
 
611
 
    /* Allocate a new server */
612
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
613
 
 
614
 
    /* Free the configuration data */
615
 
    avahi_server_config_free(&config);
616
 
 
617
 
    /* Check if creating the server object succeeded */
618
 
    if (!server) {
619
 
        fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
620
 
        returncode = EXIT_FAILURE;
621
 
        goto exit;
622
 
    }
623
 
    
624
 
    /* Create the service browser */
625
 
    if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
626
 
        fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
627
 
        returncode = EXIT_FAILURE;
628
 
        goto exit;
629
 
    }
630
 
    
631
 
    /* Run the main loop */
632
 
 
633
 
    if (debug){
634
 
      fprintf(stderr, "Starting avahi loop search\n");
635
 
    }
636
 
    
637
 
    avahi_simple_poll_loop(simple_poll);
638
 
    
639
 
exit:
640
 
 
641
 
    if (debug){
642
 
      fprintf(stderr, "%s exiting\n", argv[0]);
643
 
    }
644
 
    
645
 
    /* Cleanup things */
646
 
    if (sb)
647
 
        avahi_s_service_browser_free(sb);
648
 
    
649
 
    if (server)
650
 
        avahi_server_free(server);
651
 
 
652
 
    if (simple_poll)
653
 
        avahi_simple_poll_free(simple_poll);
654
 
 
655
 
    return returncode;
656
 
}