/mandos/release

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

« back to all changes in this revision

Viewing changes to plugins.d/mandosclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-07-21 22:51:46 UTC
  • mfrom: (15.1.4 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20080721225146-55gbo7fqocy4m930
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
                                                 gpgme_data_seek.
 (start_mandos_communication): Change "ip" arg to "const char *".  New
                               variable "written".  Remove setsockopt.
                               Bug fix: Do not change decrypted_buffer.
 (resolve_callback): Removed AVAHI_GCC_UNUSED from "interface".

Show diffs side-by-side

added added

removed removed

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